Looking to enhance your Odoo 18 module with a modern, intuitive Many2many selection field within a custom dialog popup? Whether you need a multi-product picker for sales orders or a selector for multiple records from another model, Odoo’s OWL framework provides a robust and stylish solution. In this guide, we’ll walk through the process of creating a custom dialog popup featuring a Many2many selection field in Odoo 18 using OWL. With clear, step-by-step code explanations, you’ll be able to implement this feature effortlessly and deliver a polished user experience. Let’s dive in!
Prerequisites
Before proceeding with the code, ensure you have:
- An active Odoo 18 environment.
- Familiarity with OWL (Odoo Web Library) and the basics of Odoo module development.
- A module structure configured to include JavaScript, XML templates, and client actions.
Code Breakdown
We’ll build a custom dialog popup that enables users to select multiple products from the product.template model using a Many2many field. The implementation involves three core components: a JavaScript component, an OWL template, and a client action definition. Let’s explore each part.
JavaScript Component (multiselection.js)
This JavaScript file defines the OWL component that drives the Many2many selection field.
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Component, useState } from "@odoo/owl";
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.selectionState = useState({
defaultValue: [],
displayNames: [],
relatedModel: {
label: undefined,
technical: 'product.template',
},
});
}
get domain() {
return [["active", "=", false]];
}
async onSelectionChange(resIds) {
const displayNames = await this.nameService.loadDisplayNames(
this.selectionState.relatedModel.technical,
resIds
);
this.selectionState.defaultValue = resIds;
this.selectionState.displayNames = Object.values(displayNames);
}
}
registry.category("actions").add("MultiSelectionDialog", MultiSelectionDialog);
- Module Imports: Imports key Odoo and OWL modules, such as registry for registering client actions, useService for accessing Odoo services, Component and useState from OWL, and MultiRecordSelector for the Many2many field widget.
- Component Definition: The MultiSelectionDialog class extends Component, referencing the template your_module_name.multiselection_dialog and registering MultiRecordSelector as a sub-component.
- Setup Method: Initializes the component by:
- Using the name service to retrieve display names for selected records.
- Creating a reactive selectionState object with useState to manage selected record IDs (defaultValue), their display names, and the related model (product.template).
- Domain Getter: Defines a filter ([["active", "=", false]]) to restrict selectable records (e.g., inactive products). You can adjust this to fit your needs, such as including active products.
- onSelectionChange Method: An async function triggered when records are selected, fetching display names for the selected resIds using the name service and updating selectionState with the new IDs and names.
- Registry: Registers the MultiSelectionDialog component as a client action named MultiSelectionDialog in the Odoo action registry.
OWL Template (multiselection_dialog.xml)The OWL template defines the layout of the dialog popup.?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="your_module_name.multiselection_dialog">
<MultiRecordSelector
resModel="selectionState.relatedModel.technical"
resIds="selectionState.defaultValue || []"
t-key="selectionState.relatedModel.technical"
domain="domain"
placeholder="'Select Products'"
fieldString="'Products'"
update.bind="onSelectionChange"
/>
</t>
</templates>
Explanation:
- Template Definition: The template, named your_module_name.multiselection_dialog, uses the MultiRecordSelector component to render the Many2many field.
- Attributes:
- resModel: Links to the model name (product.template) from selectionState.relatedModel.technical.
- resIds: Binds to the selected record IDs (selectionState.defaultValue), defaulting to an empty array if undefined.
- t-key: Ensures re-rendering when the model changes.
- domain: Connects to the domain getter in the JavaScript component to filter records.
- placeholder and fieldString: Set to "Select Products" and "Products" for the placeholder text and field label, respectively.
- update.bind: Triggers the onSelectionChange method when the selection changes.
Client Action (client_action.xml)
This XML file defines the client action that launches the dialog popup.
<record id="select_product_action" model="ir.actions.client">
<field name="name">Select Product</field>
<field name="tag">MultiSelectionDialog</field>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="target">new</field>
</record>
Explanation:
- Action Definition: Creates a client action with the ID select_product_action.
- Fields:
- name: Sets the action’s display name to "Select Product."
- tag: Links to the MultiSelectionDialog component registered in the JavaScript file.
- binding_model_id: Associates the action with the sale.order model, enabling access from sales order views.
- target: Set to new, opening the action in a dialog popup.
Implementation Steps
- 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.
- File Placement:
- Save the JavaScript code in static/src/js/multiselection.js.
- Save the template in static/src/xml/multiselection_dialog.xml.
- Save the client action in views/client_action.xml.
Manifest Update: Include the JavaScript and XML files in your module’s __manifest__.py:
'assets': {
'web.assets_backend': [
'your_module_name/static/src/js/multiselection.js',
'your_module_name/static/src/xml/multiselection_dialog.xml',
],
},
- Install/Upgrade Module: Install or upgrade your module in Odoo to register the action.
- Test the Action: In the Odoo interface, go to a sales order and trigger the "Select Product" action to view the dialog popup with the Many2many selection field.
Customization Tips
- Change the Model: Modify selectionState.relatedModel.technical 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 onSelectionChange method to save selected resIds to a field on the sale.order model or perform other actions.
Conclusion
Implementing a Many2many selection field in a custom dialog popup in Odoo 18 using OWL is an effective way to improve user interaction within your modules. By utilizing the MultiRecordSelector component and OWL’s reactive state management, you can craft dynamic, user-friendly interfaces with ease. The provided code serves as a strong starting point for selecting multiple records, and with minor adjustments, it can be tailored to various models and use cases. Get started, experiment, and elevate your Odoo 18 development with this powerful feature
To read more about How to Add a Many2many Selection Field in a Custom Dialog Popup in Odoo 17 Using OWL, refer to our blog How to Add a Many2many Selection Field in a Custom Dialog Popup in Odoo 17 Using OWL.