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 More” Dialog: 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.