Introduction
Ever wanted to supercharge your Odoo 17 module with a sleek, user-friendly Many2many selection field in a custom dialog popup? Whether you're building a product selector for sales orders or a multi-record picker for another model, Odoo's OWL framework makes it both powerful and elegant. In this blog, we’ll dive into creating a custom dialog popup with a Many2many selection field using OWL in Odoo 17. With step-by-step code explanations, you’ll be able to implement this feature like a pro and impress your users with a seamless interface. Let’s get started!
Prerequisites
Before we dive into the code, ensure you have:
- A working Odoo 17 environment.
- Basic knowledge of OWL (Odoo Web Library) and Odoo module development.
- A module structure set up to include JavaScript, XML templates, and client actions.
Code Breakdown
We’ll create a custom dialog popup that allows users to select multiple products (from the product.template model) using a Many2many field. The code consists of three main parts: a JavaScript component, an OWL template, and a client action definition. Let’s break them down.
1. JavaScript Component (multiselection.js)
This JavaScript file defines the OWL component that powers 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 Multiselection extends Component {
static template = 'your_module_name..multiselection';
static components = { MultiRecordSelector };
setup() {
this.nameService = useService("name");
this.relationState = useState({
defaultValue: [],
displayNames: [],
relatedModel: {
label: undefined,
technical: 'product.template',
},
});
}
get domain() {
return [["active", "=", false]];
}
async onValuesSelected(resIds) {
const displayNames = await this.nameService.loadDisplayNames(
this.relationState.relatedModel.technical,
resIds
);
this.relationState.defaultValue = resIds;
this.relationState.displayNames = Object.values(displayNames);
}
}
registry.category("actions").add("Multiselection", Multiselection);
Explanation:
- Module Imports: The code imports essential Odoo and OWL modules, including registry for registering the client action, useService for accessing Odoo services, Component and useState from OWL, and MultiRecordSelector for the Many2many field widget.
- Component Definition: The Multiselection class extends Component and links to the template your_module_name.multiselection. It also registers MultiRecordSelector as a sub-component.
- Setup Method: Initializes the component by:
- Accessing the name service to fetch display names for selected records.
- Creating a reactive relationState object using useState to store the selected record IDs (defaultValue), their display names, and the related model (product.template).
- Domain Getter: The domain method defines a filter ([["active", "=", false]]) to limit the selectable records (e.g., only inactive products). You can modify this to suit your needs, such as allowing active products.
- onValuesSelected Method: This async function is triggered when records are selected. It uses the name service to fetch display names for the selected record IDs (resIds) and updates the relationState with the new IDs and display names.
- Registry: Registers the Multiselection component as a client action named Multiselection in the Odoo action registry.
2. OWL Template (multiselection.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">
<MultiRecordSelector
resModel="relationState.relatedModel.technical"
resIds="relationState.defaultValue || []"
t-key="relationState.relatedModel.technical"
domain="domain"
placeholder="'Choose Products'"
fieldString="'Products'"
update.bind="onValuesSelected"
/>
</t>
</templates>
Explanation:
- Template Definition: The template is named your_module_name.multiselection and uses the MultiRecordSelector component to render the Many2many field.
- Attributes:
- resModel: Binds to the model name (product.template) from relationState.relatedModel.technical.
- resIds: Binds to the selected record IDs (relationState.defaultValue), with a fallback to an empty array if undefined.
- t-key: Ensures the component re-renders when the model changes.
- domain: Links to the domain getter in the JavaScript component to filter records.
- placeholder and fieldString: Set the placeholder text and field label to "Choose Products" and "Products," respectively.
- update.bind: Calls the onValuesSelected method whenever the selection changes.
3. Client Action (client_action.xml)
This XML defines the client action that triggers the dialog popup.
<record id="choose_product_action" model="ir.actions.client">
<field name="name">Choose Product</field>
<field name="tag">Multiselection</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 choose_product_action.
- Fields:
- name: Sets the action’s display name to "Choose Product."
- tag: Links the action to the Multiselection component registered in the JavaScript file.
- binding_model_id: Associates the action with the sale.order model, making it accessible from sales order views.
- target: Set to new, which opens the action in a new dialog popup.
Implementation Steps
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: Add the JavaScript and XML files to your module’s __manifest__.py:
'assets': {
'web.assets_backend': [
'your_module_name/static/src/js/multiselection.js',
'your_module_name/static/src/xml/multiselection.xml',
],
},
4. Install/Upgrade Module: Install or upgrade your module in Odoo to register the action.
5. Test the Action: In the Odoo interface, navigate to a sales order and trigger the "Choose Product" action to see the dialog popup with the Many2many selection field.
Notes on Customization
- Change the Model: Update relationState.relatedModel.technical to another model (e.g., res.partner) to select different records.
- Adjust the Domain: Modify the domain getter to filter records based on your requirements (e.g., [["active", "=", true]] for active records).
- Styling: Customize the dialog’s appearance by adding CSS in your module’s assets.
- Saving Selections: Extend the onValuesSelected method to save the selected resIds to a field on the sale.order model or perform other actions.
Conclusion
Adding a Many2many selection field in a custom dialog popup in Odoo 17 using OWL is a powerful way to enhance user interaction in your modules. By leveraging the MultiRecordSelector component and OWL’s reactive state management, you can create intuitive, dynamic interfaces with minimal effort. The code provided offers a solid foundation for selecting multiple records, and with a few tweaks, you can adapt it to various models and use cases. Dive in, experiment, and take your Odoo development to the next level with this sleek feature!
To read more about How to Create Odoo JS Dialog/Popup in Odoo 17, refer to our blog How to Create Odoo JS Dialog/Popup in Odoo 17.