Looking to improve your Odoo 19 modules with a modern, easy-to-use Many2many selection field in a custom dialog popup? Whether you want to create a multi-product selector for sales orders or allow users to choose multiple records from any model, Odoo 19’s OWL framework provides a powerful, clean, and developer-friendly solution.
In this guide, we’ll show you how to build a custom dialog popup with a Many2many selection field in Odoo 19 using OWL. This approach combines flexibility with a great user experience. Through clear, step-by-step code explanations, you’ll learn how to use OWL’s reactive state management and Odoo’s UI components to create fast, elegant, and highly interactive selection dialogs. By the end, you’ll have a reusable pattern that you can easily adapt for different use cases, helping you create polished, modern interfaces in Odoo 19. Let’s get started!
Prerequisites
Before moving forward with the code, ensure you have
- an active Odoo 19 environment,
- Familiarity with Odoo module development and basic knowledge of OWL (Odoo Web Library).
- A module structure setup with JavaScript, XML templates, and client action.
Code Breakdown
Using a Many2many field, we are going to build a custom dialogue popup that allows users to select multiple products from the product.template model. Three main parts comprise the implementation: a client action definition, an OWL template, and a JavaScript component. Let's go through each of them.
1. JavaScript Component (multiselection_dialog.js)
The OWL component that operates the Many2many selection field is defined in this JavaScript file.
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, useState } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { MultiRecordSelector } from "@web/core/record_selectors/multi_record_selector";
export class MultiSelectionDialog extends Component {
static template = "your_module_name.multiselection_dialog";
static components = { MultiRecordSelector };
setup() {
this.nameService = useService("name");
this.state = useState({
resIds: [],
displayNames: [],
model: "product.template",
});
}
get domain() {
return [["active", "=", true]];
}
async onSelectionUpdate(resIds) {
const names = await this.nameService.loadDisplayNames(
this.state.model,
resIds
);
this.state.resIds = resIds;
this.state.displayNames = Object.values(names);
}
}
// Register client action
registry.category("actions").add(
"multi_selection_dialog",
MultiSelectionDialog
);
The code imports the necessary Odoo and OWL modules, such as registry for client action registration, useService for Odoo service access, Component and useState from OWL for reactive component construction, and MultiRecordSelector for a Many2many-style record selection widget.
Component Definition: The template your_module_name.multiselection_dialog is linked to the MultiSelectionDialog class, which is an extension of Component. Additionally, MultiRecordSelector is declared as a sub-component so that the OWL template can use it.
Setup Method: The component is initialized by:
- retrieving display names for particular records by using the name service.
- The chosen record IDs (resIds), their display names, and the target model (product.template) are stored in a reactive state object created with useState.
Domain Getter: To limit the selectable records, the domain getter defines a filter ([="active", "=", true]]) that permits only active products. Depending on the needs of the business, this can be changed.
onSelectionUpdate Method: Anytime the selection is altered, this asynchronous method is called. It updates the component state with the new IDs and matching names after using the name service to retrieve display names for the chosen record IDs.
Registry: Makes the MultiSelectionDialog component callable from menus, buttons, or server actions by registering it in Odoo's action registry as a client action called multi_selection_dialog.
2. OWL Template (multiselection_dialog.xml)
The OWL template defines the structure of the dialog popup.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="your_module_name.multiselection_dialog">
<div class="o_dialog_container p-3">
<MultiRecordSelector
resModel="state.model"
resIds="state.resIds"
domain="domain"
fieldString="'Products'"
placeholder="'Select Products'"
update.bind="onSelectionUpdate"
/>
</div>
</t>
</templates>
Template Definition: An OWL template called multi_selection.multiselection_dialog is defined in the XML. This template is in charge of rendering the dialogue content and displays a Many2many-style product selection field using the MultiRecordSelector component.
Structure: To guarantee appropriate spacing and styling within a popup dialogue, the outer <div> makes use of Odoo dialog-related classes (o_dialog_container p-3).
Attributes:
- resModel: Specifies the model being queried (product.template) by binding to state.model from the JavaScript component.
- resIds: Attached to the state.resIds, which stand for the record IDs that are currently selected. This maintains the UI's alignment with the reactive state of the component.
- domain: Enables filtering of the record list (e.g., displaying only active products) by connecting to the domain getter defined in the JavaScript component.
- fieldString: Sets "Products" as the field label that appears in the user interface.
- placeholder: Describes the "Select Products" placeholder text that appears when no records are chosen.
- update.bind: Ensures that the state is updated each time the user modifies the selection by binding the onSelectionUpdate method from the JavaScript component.
3. Client Action (client_action.xml)
This XML defines the client action that triggers the dialog popup.
<odoo>
<record id="action_multi_product_selection" model="ir.actions.client">
<field name="name">Select Products</field>
<field name="tag">multi_selection_dialog</field>
</record>
<menuitem id="menu_multi_selection" name="Multi Product Selection" action="action_multi_product_selection"/>
</odoo>
Action Definition: Uses the ID action_multi_product_selection to create a client action.
Fields:
- name: Sets "Select Products" as the action's display name.
- tag: Links this action to a matching JavaScript client component by referencing the multi_selection_dialog client action tag.
- binding_model_id: Makes the action accessible from Sales Order views by binding it to the sale.order model (e.g., via an action or button).
- target: When set to new, the client action opens in a modal dialogue (popup) rather than leaving the current view.
Implementation
1. Module Structure
Ensure your odoo module (your_module_name) includes:
- A static/src/js/ folder for the JavaScript file.
- A static/src/xml/ folder for the OWL template.
- A views/ folder for the client action XML.
2. File Placement
- Save the JavaScript code in static/src/js/multiselection.js.
- Save the template in static/src/xml/multiselection.xml.
- Save the client action in views/client_action.xml.
3. Manifest Update
Include the JavaScript and XML files in your module’s __manifest__.py:
{
"name": "Multi Selection Dialog (OWL)",
"depends": ["sale_management"],
"data": [
"views/multi_client_action.xml",
],
"assets": {
"web.assets_backend": [
"multi_selection/static/src/js/multiselection_dialog.js",
"multi_selection/static/src/xml/multiselection_dialog.xml",
],
},
"installable": True,
"application": False,
}4. Install/Update Module
Install or upgrade your module in Odoo to register the action.
5. Test Action
In the Odoo interface, navigate to a sales order and trigger the "Choose Product" action to see the dialog pop-up with the Many2many selection field.
Customization Tips
- Change the Model: Update this.state.state.model in setup() to another model (e.g., res.partner) to select different types of records.
- Adjust the Domain: Update the domain getter to filter records based on your criteria (e.g., [["active", "=", true]] for active records).
- Styling: Enhance the dialog’s appearance by adding CSS to your module’s assets.
- Saving Selections: Extend the onSelectionUpdate(resIds) method to save selected resIds to a field on the sale.order model or perform other actions.
A Many2many selection field implemented in a custom dialog popup using OWL is an engaging and effective way of interacting with the user in Odoo 19. By utilizing the MultiRecordSelector component and OWL's reactive state management, developers can create simple, dynamic, and user-friendly interfaces with very little effort. In this way, the selection of multiple records in a modal dialog has been made easier for users, and thus the overall efficiency of the workflow has been increased.
The implementation given above is a good starting point for the selection of multiple records, and it is also a good foundation that can be extended or adapted easily to comply with the requirements of different models and businesses. Little changes like changing the target model, refining domains, or persisting selected records can help you to use this solution for a wide range of use cases. OWL will, regardless of you creating advanced selection dialogs or custom popups, help you in building clean, modern UI components that enhance your Odoo modules.
Do more exploration, do more experimenting with features, and make the most out of OWL and Odoo's JavaScript framework by building strong, interactive features that will be the next level of your Odoo development.
To read more about How to Add a Many2many Selection Field in a Custom Dialog Popup in Odoo 18 Using OWL, refer to our blog How to Add a Many2many Selection Field in a Custom Dialog Popup in Odoo 18 Using OWL.