如何在Odoo销售订单行特定列添加Popover?(One2ManyListView继承)
Fixing Popover for Odoo One2Many ListView Rows
Hey there, let's dig into why your One2Many ListView popover code isn't working and get it sorted out!
The Core Issues With Your Current Code
Your approach works for FormView because the do_show hook fires after the form's DOM is fully rendered, but for One2Many ListViews, there are two key problems:
- Incorrect Hook Timing: The
do_showmethod doesn't guarantee the ListView's table rows (tr/td) are already rendered. When you callthis.x2m.$el.find('tr'), those elements simply don't exist yet. - Nested Include Anti-Pattern: Including the ListView class inside the FieldOne2Many's
initmethod is problematic—it will re-apply the include every time a One2Many field initializes, leading to duplicate bindings and unexpected behavior.
Working Solution
Here's a revised approach that targets the right lifecycle hooks and ensures the DOM is ready before initializing popovers:
odoo.define("saleorderline_on_hover.One2ManyPopover", function (require) { "use strict"; var core = require('web.core'); var FieldOne2Many = core.form_widget_registry.get('one2many'); var ListView = require("web.ListView"); // Extend ListView to add popover initialization logic ListView.include({ init: function() { this._super.apply(this, arguments); // Only target sale.order.line list views (adjust as needed) if (this.dataset.model === 'sale.order.line') { // Listen for the 'rendered' event—fired after the list is fully rendered this.on('rendered', this, this._initRowPopovers); } }, _initRowPopovers: function() { // Use the ListView's own $el to find rows (no need for x2m reference) this.$el.find('tr').popover({ content: function() { // Customize your popover content here—you can access row data too! return '<p>SHOW INFORMATION HERE</p>'; }, html: true, placement: function(context, source) { return $(source).position().top < 200 ? 'bottom' : 'top'; }, trigger: 'hover' }); } }); // Optional: Add extra control by targeting only the sale.order's order_line field FieldOne2Many.include({ start: function() { var result = this._super.apply(this, arguments); // Check if we're on the sale.order form and targeting the order_line field if (this.view.dataset.model === 'sale.order' && this.field.name === 'order_line') { var listViewInstance = this.x2many_views.list; // Ensure popovers are reinitialized if the list refreshes listViewInstance.on('rendered', listViewInstance, listViewInstance._initRowPopovers); } return result; } }); });
Key Improvements Explained
- Lifecycle Event: We use the
renderedevent of ListView, which fires only after the table and its rows are fully rendered in the DOM—sofind('tr')will reliably locate elements. - Targeted Scope: We check the model (
sale.order.line) to ensure popovers only apply to sales order lines, not all One2Many lists. - Clean Extension: Separating the ListView extension from FieldOne2Many avoids duplicate bindings and makes the code easier to maintain.
Bonus: Target Specific Columns
If you want popovers only on a specific column (instead of entire rows), modify the selector in _initRowPopovers:
// Replace 'td.o_field_widget[name="product_id"]' with your target column's selector this.$el.find('td.o_field_widget[name="product_id"]').popover({ /* ... */ });
You can get the correct selector by inspecting the column in your browser's dev tools.
内容的提问来源于stack exchange,提问作者Zoubida
相关产品推荐
相关产品推荐

