Odoo 18’s OWL (Odoo Web Library) framework empowers developers to build dynamic, user-friendly interfaces with ease. A frequent need in Odoo modules is to include a Many2one field in a custom popup dialog, allowing users to choose a single record (e.g., a product from product.product) from a related model. This guide walks you through building a sleek, interactive popup with a Many2one field using OWL in Odoo 18. It’s perfect for developers aiming to enhance their modules with responsive, professional-grade dialogs.
Benefits of a Many2one Field in a Popup
A Many2one field links a record to one record in another model, such as selecting a customer for a task or a product for an order. When embedded in a popup, it offers a streamlined dropdown or search-enabled interface, ideal for:
- Choosing a product in a wizard for sales processing.
- Assigning a partner or category in a custom interface.
- Simplifying data selection within a dialog.
This tutorial demonstrates how to create a popup dialog that lets users select a product using a Many2one field, styled with Odoo 18’s modern conventions and activated via a client action.
Prerequisites
Ensure you have:
- Odoo 18 set up and operational.
- Familiarity with Odoo module development, including Python, XML, and JavaScript (OWL).
- A custom module (e.g., named your_module).
- Understanding of OWL components and the Many2XAutocomplete widget for relational fields.
Let’s get started!
Step 1: Build the OWL Component
The popup’s core is an OWL component that uses the Many2XAutocomplete widget to render a searchable Many2one field. Below is the JavaScript code, tailored for Odoo 18.
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Many2XAutocomplete } from "@web/views/fields/relational_utils";
export class ProductSelectorPopup extends Component {
static components = { Many2XAutocomplete };
static template = "your_module.ProductSelectorPopup";
setup() {
this.state = useState({ selectedProduct: "Choose a Product" });
this.orm = useService("orm");
}
async onSelectionChange(selection) {
this.state.selectedProduct = selection[0]?.display_name || "Choose a Product";
}
getRecordDomain() {
return [["active", "=", true]];
}
async saveSelection() {
if (this.state.selectedProduct !== "Choose a Product") {
await this.orm.write("some.model", [/* record ID */], {
product_id: this.state.selectedProduct.id
});
this.props.close();
}
}
}
registry.category("client_actions").add("product_selector_popup", ProductSelectorPopup);
Code Breakdown:
Imports:- Component, useState: OWL 2.0 utilities for reactive components.
- registry: Registers the component in Odoo 18’s client action registry.
- useService: Accesses Odoo 18 services like ORM.
- Many2XAutocomplete: Handles Many2one field rendering with autocomplete.
Class:- ProductSelectorPopup extends Component to define the popup’s logic.
- static components and static template link to the widget and XML template.
- setup() initializes a reactive state with selectedProduct and the ORM service.
Methods:- onSelectionChange(selection): Updates selectedProduct with the selected record’s label (Odoo 18 uses label for autocomplete selections).
- getRecordDomain(): Filters records (e.g., only active products).
- saveSelection(): Saves the selected product to a model (replace some.model and record ID with your context).
Registration:- The component is registered under client_actions, aligning with Odoo 18’s conventions.
Step 3: Define the OWL Template
The OWL template defines the popup’s UI, using Many2XAutocomplete for the Many2one field and Odoo 18’s utility classes for styling.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="your_module.ProductSelectorPopup">
<div class="p-4 bg-light">
<label for="product-selector" class="form-label fw-bold mb-2 text-dark">
Select a Product
</label>
<div id="product-selector" class="bg-white p-2 rounded">
<Many2XAutocomplete
value="state.selectedProduct"
resModel="'product.product'"
fieldString="'Product'"
getDomain.bind="getRecordDomain"
update.bind="onSelectionChange"
activeActions="{}"
isToMany="false"
/>
</div>
<button class="btn btn-primary mt-3" t-on-click="saveSelection">Confirm</button>
</div>
</t>
</templates>
Template Breakdown:
- Structure:
- <t t-name="your_module.ProductSelectorPopup"> defines the template.
- A <div> with p-4 bg-light provides padding and a light background.
- Label:
- A <label> with form-label fw-bold mb-2 text-dark creates a bold, clear title.
- Many2XAutocomplete:
- t-att-value="state.selectedProduct": Binds the display value to state.
- resModel="'product.product'": Targets the product model.
- fieldString="'Product'": Sets a user-friendly label.
- t-att-getDomain and t-att-update: Bind the domain and selection handlers.
- activeActions="{}": Disables actions like creating new records.
- isToMany="false": Specifies a Many2one field.
- Button:
- A <button> with btn btn-primary mt-3 triggers saveSelection.
- Styling:
- Odoo 18 utility classes (bg-white, p-2, rounded) ensure a modern, clean UI.
Step 4: Trigger the Popup with a Client Action
To trigger the popup, create a client action in custom_views.xml:
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="action_product_selector_popup" model="ir.actions.client">
<field name="name">Product Selector Popup</field>
<field name="tag">product_selector_popup</field>
</record>
<menuitem
id="menu_product_selector_popup"
name="Open Product Selector"
action="action_product_selector_popup"
parent="your_module.menu_root"
sequence="10"/>
</odoo>
Explanation:
- Their.actions.client record defines the client action with the tagproduct_selector_popup, matching the registry entry.
- The <menuitem> adds a menu item to trigger the popup (adjust parent to your module’s menu structure).
Step 5: Testing the Popup
- Install or upgrade your_module in Odoo 18.
- Navigate to the menu item (e.g., “Open Product Selector”).
- The popup should display with a searchable Many2one field for selecting products.
- Select a product and click “Confirm” to save (ensure some.model and record ID are configured).
Debugging Tip: If the popup doesn’t appear, check the browser console for JavaScript errors and verify asset loading in __manifest__.py.
Conclusion
Adding a Many2one selection field to a custom popup dialog in Odoo 18 using OWL is a powerful way to create intuitive, user-friendly interfaces. The Many2XAutocomplete widget provides a seamless dropdown experience, while Odoo 18’s OWL 2.0 framework and utility classes ensure a modern, responsive design. This tutorial delivers a complete implementation, including a reactive OWL component, a styled template, a client action, and module setup. You can extend this further by:
- Customizing the domain to filter specific records.
- Adding validation or error messages in saveSelection.
- Enhancing the UI with additional fields or buttons.
By following these steps, you can elevate your Odoo module’s functionality, streamline user interactions, and deliver a professional-grade experience tailored to your business needs.
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