Enable Dark Mode!
overview-of-multirecordselector-and-reusable-owl-widgets-in-odoo-19.jpg
By: Abhijith CK

Overview of MultiRecordSelector and Reusable OWL Widgets in Odoo 19

Technical Odoo 19 Owl

The migration of Odoo to the Odoo Web Library (OWL) has completely revolutionized the development of frontend applications within the Odoo ecosystem. The current version of Odoo – Odoo 19 – has reached maturity, boasting an extremely powerful component-based framework in the form of OWL. For Odoo developers, the availability of strong core UI components is one of the best things that comes with the mature framework.

Instead of developing inputs, drop downs, and pagination functionalities from scratch, you can take advantage of the native and highly optimized widgets provided by Odoo. In this blog post, we are going to take a look at the core selector components - focusing on MultiRecordSelector - along with some other important UI components such as RecordSelector, RecordAutocomplete, TagsList, Notebook, and Pager.

1. Selecting Multiple Records: The MultiRecordSelector

MultiRecordSelector is an important tool that helps you choose multiple database records for a certain model. You do not have to code relational fields and input by yourself; instead, you should use this component in client actions, reports, and configuration pages.

Components inside:

MultiRecordSelector consists of three main components:

  • RecordAutocomplete: Search box.
  • TagsList: List of the selected items with interactive tags or avatars.
  • SelectCreateDialog: Modal popup that appears when the user chooses the "Search More..." option from the dropdown menu, making it possible to conduct an advanced searching feature.

Props:

The component receives these props:

static props = {
    resIds: { type: Array, element: Number }, // Array of selected record IDs
    resModel: String,                         // Technical name of the Odoo model (e.g., 'res.partner')
    update: Function,                         // Callback triggered when selections are updated
    domain: { type: Array, optional: true },   // Domain filters applied to the search queries
    context: { type: Object, optional: true }, // Context dictionary sent to name_search ORM call
    fieldString: { type: String, optional: true }, // Field label used in "Search More" modal header
    placeholder: { type: String, optional: true }, // Placeholder text displayed in the input field
};

Example

To use MultiRecordSelector in your template, you can declare it in your XML and bind the properties in your JS file:

XML Template):

<templates xml:space="preserve">
    <t t-name="my_custom_module.FilterPanel">
        <div class="my-custom-filter o_input d-flex align-items-center gap-2">
            <span class="fw-bold">Filter Partners:</span>
            <MultiRecordSelector 
                resModel="'res.partner'" 
                resIds="state.partnerIds" 
                update.bind="onPartnersUpdated" 
                domain="[['is_company', '=', true]]"
                placeholder="'Select companies...'"
            />
        </div>
    </t>
</templates>

OWL Component:

import { Component, useState } from "@odoo/owl";
import { MultiRecordSelector } from "@web/core/record_selectors/multi_record_selector";
export class FilterPanel extends Component {
    static template = "my_module.FilterPanel";
    static components = { MultiRecordSelector };
    setup() {
        this.state = useState({
            partnerIds: [],
        });
    }
    onPartnersUpdated(newIds) {
        this.state.partnerIds = newIds;
        // Perform filtering logic or trigger page reload based on selected IDs
        console.log("Selected Partners:", this.state.partnerIds);
    }
}

2. Supporting Components

Odoo implements the MultiRecordSelector component that is based on a few OWL components. These OWL components can also be individually used.

A. RecordSelector:

For selecting a single record, Odoo provides the RecordSelector component. This component behaves like the MultiRecordSelector component. The only difference is that it gives the ID of one record (or false if cleared).

static props = {
    resId: [Number, { value: false }], // Selected record ID or false
    resModel: String,                  // Target model name
    update: Function,                  // Callback receiving the new ID (or false)
    domain: { type: Array, optional: true },
    context: { type: Object, optional: true },
    fieldString: { type: String, optional: true },
    placeholder: { type: String, optional: true },
};

Example of usage

<templates xml:space="preserve">
   <t t-name="my_module.DashboardFilter">
       <div class="p-3 bg-light border rounded">
           <label class="form-label fw-bold">Select Responsible User:</label>
           <RecordSelector
               resModel="'res.users'"
               resId="state.selectedUserId"
               update.bind="onUserSelected"
               placeholder="'Search or select a user...'"
               domain="[['share', '=', false]]"
           />
       </div>
   </t>
</templates>

B. RecordAutocomplete:

Both of them make use of the RecordAutocomplete component that communicates with the backend. The thing is, this component makes a listener for the user’s keystrokes, and then calls the Odoo ORM name_search function:

search(name, limit) {
    const domain = this.getDomain();
    return this.orm.call(this.props.resModel, "name_search", [], {
        name,
        domain: domain,
        limit,
        context: this.props.context || {},
    });
}

Example of individual usage

Search MoreDialog: When the search limit is reached, a “Search More…” button will appear in the dropdown, and by clicking on it, the global dialogs registry will open the built-in Odoo many2many/many2one dialog box.

C. TagsList:

This component renders selection badges.

  • Truncate Smartly: While picking multiple tags, one can limit their display by specifying visibleItemsLimit. All the extra tags will get hidden automatically by using a badge (+5 or 9+), which shows up as a tooltip with a description for all the hidden tags.
  • Badges with Avatars: In case the model used supports avatars (res.partner, res.users, hr.employee), it will pick up an avatar (avatar_128) for the user and render accordingly.

Example of individual usage

<templates xml:space="preserve">
   <t t-name="my_module.TagsDemo">
       <div class="tags-container p-3 border">
           <h5 class="mb-2">Active Tags:</h5>
           <TagsList
               tags="state.tags"
               visibleItemsLimit="3"
           />
       </div>
   </t>
</templates>

3. Other Core Reusable OWL Widgets

Odoo 19 offers a wide range of generic layout widgets that make frontend development much easier.

A. The Notebook Component (@web/core/notebook/notebook):

Notebook is a component that provides for a standard tab navigator. Sheets in Odoo are divided into "Tabs" or "Pages." In OWL, pages can be defined dynamically through array objects or statically via template slots:

<Notebook defaultPage="'general_info'">
    <t t-set-slot="general_info" title="'General Info'" isVisible="true">
        <div>
            <!-- Content for General Info Tab -->
        </div>
    </t>
    <t t-set-slot="settings" title="'Settings'" isVisible="true">
        <div>
            <!-- Content for Settings Tab -->
        </div>
    </t>
</Notebook>

It includes the field validation check by asking env.model if fields inside non-visible tabs are not valid, marking the tab name with an error sign if it is the case.

B. The Pager Component (@web/core/pager/pager):

The Pager is Odoo's native paging widget that shows the current offset and limit numbers (e.g., 1-80 out of 320). It allows editing its values via clicks, letting users input their own ranges (e.g., 20-40) and also supports clicks on "prev" and "next".

<Pager 
    offset="state.offset" 
    limit="state.limit" 
    total="state.total" 
    onUpdate.bind="onPagerUpdate"
/>

Key Benefits of Using Native OWL Widgets

  • Design Consistency: The use of widgets in your modules makes them consistent with the design of Odoo itself.
  • Security: The native widgets automatically take into account record rules and ACL of Odoo since all their queries are executed through the ORM client of the user session.
  • Automatic Performance Improvement: Widgets such as RecordAutocomplete include features like debouncing and request-cancelling for search optimization on the server side.
  • Upgrade Compatibility: Because they are managed by Odoo, updating your modules when using new Odoo versions is much easier.

Construction of modern, quality front-end applications using Odoo 19 becomes much more efficient by using the collection of OWL components. Using the component of MultiRecordSelector makes available a versatile multi-selection widget complete with autocomplete functionality, tag display capability, and modal selections. Integrating this with other widgets, such as Notebook and Pager, makes the construction of efficient UIs easier.

To read more about Complete Overview of Widgets in Odoo 19, refer to our blog Complete Overview of Widgets in Odoo 19.


Frequently Asked Questions

How do I pass context parameters to the MultiRecordSelector?

You can give custom context objects directly through the component using the context property. The component then passes on this context object to the backend search RPC call, which helps when you have to invoke some special python domains, default values, or model filters:

What happens if a record selected in MultiRecordSelector is deleted or inaccessible?

If one of the IDs passed in resIds points to a deleted record or a record the current user has no rights to read, Odoo's name service returns an error or an empty value. The widget handles this gracefully by checking if the display name exists, rendering Inaccessible/missing record ID: as the tag title if not.

How do I set the number of tags visible in TagsList before it collapses into "+X"?

You can pass visibleItemsLimit (Number) inside the properties of the component containing the TagsList. In MultiRecordSelector.xml, it uses the standard , which has default parameters, but you can create a custom version or extend the template to add ‘visibleItemsLimit’ attribute and a number for the limit.

How does RecordAutocomplete prevent outdated search results when typing fast?

RecordAutocomplete utilizes Odoo's abortable promises (lastProm.abort(false)). When a search starts, it checks if this.lastProm exists. If a request is already running, it aborts it before launching the next name_search RPC query. This ensures only the most recent search request updates the state, resolving potential race conditions.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



WhatsApp
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

Send Us A Message