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