Skip to Content
OWL forms

Property Definition Selection

Odoo 19 OWL component — Property Definition Selection (views)

Live preview Interactive
Source excerpt web/static/src/views/fields/properties/property_definition_selection.js
import { useService } from "@web/core/utils/hooks";
import { uuid } from "@web/core/utils/strings";

import { Component, useState, useRef, useEffect } from "@odoo/owl";
import { useSortable } from "@web/core/utils/sortable_owl";

export class PropertyDefinitionSelection extends Component {
    static template = "web.PropertyDefinitionSelection";
    static props = {
        default: { type: String, optional: true },
        options: {},
        readonly: { type: Boolean, optional: true },
        canChangeDefinition: { type: Boolean, optional: true },
        onOptionsChange: { type: Function, optional: true }, // we add / remove / rename an option
        onDefaultOptionChange: { type: Function, optional: true }, // we select a default value
    };

    setup() {
        this.notification = useService("notification");

        // when we create a new option, it's added in the state
        // when we have finished to edit it (blur / enter) we propagate
        // the new value in the props
        this.state = useState({
            newOption: null,
        });

        this.propertyDefinitionSelectionRef = useRef("propertyDefinitionSelection");
        this.addButtonRef = useRef("addButton");

        useEffect(() => {
            // automatically give the focus to the new option if it is empty
            if (!this.state.newOption) {
                return;
            }
            const inputs = this.propertyDefinitionSelectionRef.el.querySelectorAll(
                ".o_field_property_selection_option input"
            );
            if (inputs && inputs.length && !inputs[this.state.newOption.index].value) {
                inputs[this.state.newOption.index].focus();
            }
        });

        useSortable({
            enable: () => this.props.canChangeDefinition && !this.props.readonly,
            ref: this.propertyDefinitionSelectionRef,
            handle: ".o_field_property_selection_drag",
            elements: ".o_field_property_selection_option",
            cursor: "grabbing",
            onDrop: async ({ element, previous }) => {
                const movedOption = element.getAttribute("option-name");
                const destinationOption = previous && previous.getAttribute("option-name");
                await this.onOptionMoveTo(movedOption, destinationOption);
            },
        });
    }

    /* --------------------------------------------------------
     * Public methods / Getters
     * -------------------------------------------------------- */

    /**
     * Return the current available options.
     *
     * Make a deep copy to not change original object to be able to restore
     * the old props if we discard the editing of the forma view.
     *
     * @returns {array}
     */
    get options() {
        return JSON.parse(JSON.stringify(this.props.options || []));
    }

    /**
     * Options visible by the UI, include the newly created option if needed.
     *
     * @returns {array}
     */
    get optionsVisible() {
        const options = this.options || [];
        const newOption = this.state.newOption;
        if (newOption) {
            options.splice(newOption.index, 0, [newOption.name, ""]);
        }
        return options;
    }

    /* --------------------------------------------------------
     * Event handlers
     * -------------------------------------------------------- */

    /**
     * Add a new empty selection option.
     */
    onOptionCreate(index) {
        this.state.newOption = {
            index: index,
            name: uuid(),
        };
    }

    /**
     * We changed an option label.
     *
     * @param {event} event
     * @param {integer} optionIndex
     */
    onOptionChange(event, optionIndex) {
        const target = event.target;
        const newLabel = target.value;

        if (this.options[optionIndex] && this.options[optionIndex][1] === newLabel) {
            // do not update the props if we are already up to date
            // e.g. we pressed enter already and lost focus
            return;
        }

        const options = this.optionsVisible;

        if (!newLabel || !newLabel.length) {
Registry / API
Registry name
PropertyDefinitionSelection
Category
Module
web
Slug
property-definition-selection
Nav group
forms