In Odoo 17, adding custom buttons to the header of a Kanban or List view is a common UI customization request. This article demonstrates two methods for adding buttons to the List View header in Odoo 17.
Method 1: Using a Custom Controller
This method involves creating a custom controller and referencing it using js_class
in the view.
XML Template (listview_buttons.xml
)
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="hr_plus.ListView.Buttons" t-inherit="web.ListView.Buttons">
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
<button type="button" class="btn btn-primary" style="margin-left:10px;" t-on-click="OnCustomClick">
Custom Button
</button>
</xpath>
</t>
</templates>
JavaScript Controller (listview_buttons.js
)
/** @odoo-module **/
import { ListController } from "@web/views/list/list_controller";
import { registry } from "@web/core/registry";
const listView = registry.category("views").get("list");
export class ListViewButtonsController extends ListController {
setup() {
super.setup();
}
OnCustomClick() {
alert("Clicked");
}
}
registry.category("views").add("list_view_button", {
...listView,
Controller: ListViewButtonsController,
buttonTemplate: "hr_plus.ListView.Buttons",
});
Manifest Configuration (__manifest__.py
)
'assets': {
'web.assets_backend': [
'hr_plus/static/src/xml/listview_buttons.xml',
'hr_plus/static/src/js/listview_buttons.js',
],
}
View XML
<record id="hr_plus_view_attendance_tree_inherited" model="ir.ui.view">
<field name="name">hr_plus_view_attendance_tree_inherited</field>
<field name="model">hr.attendance</field>
<field name="inherit_id" ref="hr_attendance.view_attendance_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="js_class">list_view_button</attribute>
</xpath>
</field>
</record>
Method 2: Using OWL Template with props.resModel
This approach uses conditional rendering inside the XML template and OWL's patch()
method.
XML Template (listview_buttons.xml
)
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="hr_plus.ListView.Buttons" t-inherit="web.ListView.Buttons" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
<t t-if="props.resModel == 'hr.attendance'">
<button type="button" class="btn btn-primary" style="margin-left:10px;" t-on-click="OnCustomClick">
Custom Button 1
</button>
</t>
<t t-if="props.resModel == 'hr.department'">
<button type="button" class="btn btn-primary" style="margin-left:10px;" t-on-click="OnCustomClick2">
Custom Button 2
</button>
</t>
</xpath>
</t>
</templates>
JavaScript Patch (listview_buttons.js
)
/** @odoo-module **/
import { patch } from '@web/core/utils/patch';
import { ListController } from '@web/views/list/list_controller';
patch(ListController.prototype, {
setup() {
super.setup();
},
OnCustomClick() {
alert("Clicked");
},
OnCustomClick2() {
alert("Click2");
}
});
Summary
Feature | Method 1 | Method 2 |
---|---|---|
Uses Custom Controller | ✅ Yes | ❌ No |
Uses OWL Patch | ❌ No | ✅ Yes |
Supports Multiple Models | ⚠️ Requires logic | ✅ With props.resModel |
Easier to Scale | ✅ | ✅ |
You can choose either method based on your architecture preference and flexibility requirements.
Reply