In Odoo, the List/Tree view is used to view all records in the form of a table. Each row represents a unique record in the database, and each column represents the important fields of that model. By using tree view, we have the option to search, filter and group the records. Basically, the default list view is defined in ListRender templates of web modules.
In this blog, we are going to discuss how to extend the list/tree view of Odoo 16. As an example, we want to add a pin option to the list view.
Step 1: Add pin icon in each row of the tree view
The table of list view is defined in ListRender templates inside the web directory. So, first, we want to inherit that template using the following XML code.
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="list_tree_pin_record.AllHeadListRender" owl="1"
t-inherit="web.ListRenderer" t-inherit-mode="extension">
<xpath expr="//table/thead/tr/th[@t-if='hasSelectors']" position="before">
<th/>
</xpath>
</t>
<t t-name="list_tree_pin_record.RowListView"
t-inherit="web.ListRenderer.RecordRow" to-inherit-mode="extension"
owl="1">
<xpath expr="//td[hasclass('o_list_record_selector')]" position="before">
<t t-if="hasSelectors">
<td>
<i class="fa fa-thumb-tack" style="color: rgb(60, 179, 113);"
t-on-click="(ev) => this.pin_record(ev,record)"/>
</td>
</t>
</xpath>
</t>
</templates>
In this, we added the table head before the selection field and added a pin icon in each row by inheriting the ‘ListRenderer.RecordRow’ template. And set a function on t-on-click.
2. Add JavaScript code to change the color of pinned records
Now, we need to add JavaScript code to change the color of the row by clicking the pin icon and aligning the pinned row at the top of the list.
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { ListRenderer } from "@web/views/list/list_renderer";
const { onMounted } = owl;
var rpc = require('web.rpc');
var array = []
//patch the ListRenderer template to store and view pinned records
patch(ListRenderer.prototype, '/list_tree_pin_records/static/src/js/pin_records.js', {
pin_record(ev,record){
var row = $(event.target).parent().parent()[0]
var index =array.indexOf(record.resId);
if (index != -1){
row.style.background='white',
delete array[index]
}
else {
$($(event.target).parent().parent().parent()[0]).prepend(row)
array.push(record.resId)
row.style.background='#4BC8'
}
var self = this;
rpc.query({//rpc query to store pinned records in database
model: "pin.records",
method: 'save_pin_record',
args: [[parseInt(record.resId),record.resModel]],
}).then(function (data) {
});
}
});
In this code, add the click function of the pin icon by patching the ListRenderer class. By clicking the pin icon, the color of the row is changed and it will align at the top of the tree view. A double click of the pin icon results in the unpinning of records, and the color is changed back to white. To store the pinned records in a custom module, we use an RPC call, and the record id and record model are passed as parameters. After refreshing, the pinned records are moved back to their original state. To avoid this, we added a function in OnMounted hooks. The following code is added to the JS code.
setup() {
this._super.apply();
onMounted(this.pin)
},
pin(){
var current_model = this.props.list.model.env.searchModel.resModel
var table_row = $('.o_data_row')
var row = this.props.list.records
rpc.query({// rpc query to view pinned records at the top of the table.
model: "pin.records",
method: "get_pin_record",
args: [current_model],
}).then(function (result) {
for (var num=0;num<=table_row.length-1;num++){
var row_id = row[num].resId
result.forEach(element = > {
if (row_id == element.id){
table_row[num].style.background = '#4BC8'
table_row[num].parentNode.insertBefore(table_row[num],table_row[0])}
})
}
});},
In this code, we added a function ‘pin’ on OnMounted hooks. In this function, we used an RPC call to get pinned records from the custom model, where we stored pinned records. Here, the current model is passed as a parameter. Then check the current records with rpc returned pinned records. If it is matched, the color of that record row is changed, and it will align at the top of the list view.
3. A custom module for storing pinned records.
To get all pinned records when refreshing the page, first, we want to store the pinned record when clicking the pin icon on the tree view. For that purpose, we added a new model.
from odoo import api, fields, models
class PinRecord(models.Model):
"""Class is used to store pinned records in the database"""
_name = 'pin.records'
_description = 'Pin Records'
record_id = fields.Integer(string="Record Id", help='Record id')
res_model = fields.Char(string="Model", help='Corresponding model')
user_id = fields.Many2one('res.users', string='User',
help="Set current user")
@api.model
def save_pin_record(self, pin_model):
"""Function to create records in model"""
record = self.search([('record_id', '=', pin_model[0]),
('res_model', '=', pin_model[1])])
if record:
record.unlink()
else:
self.create({
'record_id': pin_model[0],
'res_model': pin_model[1],
'user_id': self.env.uid
})
return True
@api.model
def get_pin_record(self, pin_model):
"""Function to fetch id of pinned records."""
record_ids = self.search([('res_model', '=', pin_model),
('user_id', '=', self.env.uid)])
pinned_record = [{'id': rec.record_id,
'model': rec.res_model} for rec in
record_ids]
return pinned_record
In this model, the record ID and corresponding model of records are stored. When clicking the pin icon it will create a new record in this model by rpc method ‘save pin record’. The clicking of pinned records results in unlinking of corresponding records. When refreshing the page, another rpc method ‘get pin record’ will return all pinned records of the corresponding model.
By using the ListRenderer template and ListRenderer class, we can customize the list/tree view of Odoo, which improves the user experience for Odoo users.