Skip to Content
OWL forms

Property Tags Field

Odoo 19 OWL component — Property Tags Field (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
PropertyTagsField
Category
Module
web
Slug
property-tags-field
Nav group
forms