fields
forms
Property Tags
Odoo 19 fields — Property Tags (views)
Live preview
Interactive
Source excerpt
web/static/src/views/fields/properties/property_tags.js
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
import { ColorList } from "@web/core/colorlist/colorlist";
import { _t } from "@web/core/l10n/translation";
import { usePopover } from "@web/core/popover/popover_hook";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { TagsList } from "@web/core/tags_list/tags_list";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { useTagNavigation } from "@web/core/record_selectors/tag_navigation_hook";
import { Component } from "@odoo/owl";
class PropertyTagsColorListPopover extends Component {
static template = "web.PropertyTagsColorListPopover";
static components = {
ColorList,
};
static props = {
colors: Array,
tag: Object,
switchTagColor: Function,
close: Function,
};
}
export class PropertyTags extends Component {
static template = "web.PropertyTags";
static components = {
AutoComplete,
TagsList,
ColorList,
Popover: PropertyTagsColorListPopover,
};
static props = {
id: { type: String, optional: true },
selectedTags: {}, // Tags value visible in the tags list
tags: {}, // Tags definition visible in the dropdown
// Define the behavior of the delete button on the tags, either
// "value" or "tags". If "value", the delete button will unselect
// the value, if "tags" the value will be removed from the definition.
deleteAction: { type: String },
readonly: { type: Boolean, optional: true },
canChangeTags: { type: Boolean, optional: true },
// Select a new value
onValueChange: { type: Function, optional: true },
// Change the tags definition (can also receive a second
// argument to update the current selected value)
onTagsChange: { type: Function, optional: true },
};
setup() {
this.notification = useService("notification");
this.popover = usePopover(this.constructor.components.Popover);
useTagNavigation("propertyTags", {
delete: (index) => this.deleteTagByIndex(index),
});
}
/* --------------------------------------------------------
* Public methods / Getters
* -------------------------------------------------------- */
/**
* Return true if we should display the badges or just the tag label.
*
* @returns {array}
*/
get displayBadge() {
return !this.env.config || this.env.config.viewType !== "kanban";
}
/**
* Return the list containing tags values and actions for the TagsList component.
*
* @returns {array}
*/
get tagListItems() {
if (!this.props.selectedTags || !this.props.selectedTags.length) {
return [];
}
// Retrieve the tags label and color
// ['a', 'b'] => [['a', 'A', 5], ['b', 'B', 6]]
let value = this.props.tags.filter((tag) => this.props.selectedTags.indexOf(tag[0]) >= 0);
if (!this.displayBadge) {
// in kanban view e.g. to not show tag without color
value = value.filter((tag) => tag[2]);
}
const canDeleteTag =
!this.props.readonly &&
(this.props.canChangeTags || this.props.deleteAction === "value");
return value.map((tag) => {
const [tagId, tagLabel, tagColorIndex] = tag;
return {
id: tagId,
text: tagLabel,
className: this.props.canChangeTags ? "" : "pe-none",
colorIndex: tagColorIndex || 0,
onClick: (event) => this.onTagClick(event, tagId, tagColorIndex),
onDelete: canDeleteTag && (() => this.onTagDelete(tagId)),
};
});
}
/**
* Return the current selected tags.
* Make a deep copy to not make change on the original object
* and to be able to discard change.
*
* @returns {array}
*/
get selectedTags() {
return JSON.parse(JSON.stringify(this.props.selectedTags || []));
}
/**
* Return the current tags that can be selected.
Registry / API
- Registry name
property_tags- Category
fields- Module
web- Slug
property-tags- Nav group
forms