Odoo Color one2many Treeview Line

317 Views Asked by At

can anyone help me get my case to run? I want to color the line of a many2one treeview by the value of a field inside the line.

  <field colspan="2" name="timesheet_ids" class="custom-field" widget="one2many"
                                   nolabel="1">

                                <tree editable="bottom"  >
                                    <field name="sequence" colors="red:is_line_nonsense == True"/>
                                    <field name="time_event_type"/>
                                    <field name="date" attrs="{'readonly': [('is_date_readonly', '=', True)]}"/>
                                    <field name="is_begin_readonly" invisible="1"/>
                                    <field name="is_end_readonly" invisible="1"/>
                                    <field name="is_date_readonly" invisible="1"/>
                                    <field name="is_line_nonsense" invisible="1"/>

                                    <field name="begin_time"
                                           attrs="{'readonly': [('is_begin_readonly', '=', True)]}"
                                           widget="float_time"/>
                                    <field name="end_time" attrs="{'readonly': [('is_end_readonly', '=', True)]}"
                                           widget="float_time"/>
                                    <field name="total_hours" widget="float_time" readonly="true"/>
                                </tree>
                            </field>

I want that the line bg gets red when the field is_line_nonsense = true.

Here is the JS code.

odoo.define('chn_events.highlight_rows', function (require) {
    "use strict";
 var ListRenderer = require('web.ListRenderer');
    ListRenderer.include({
        _renderRow: function (record, index) {
            var $row = this._super.apply(this, arguments);
            var color = record.data.color;
            if (color) {
                $row.css('background-color', color);
            }
            return $row;
        },
    });
});

but it doesn't work, the script is loaded but no color will be shown.

I also tried with a module. https://odoo-community.org/shop/colorize-field-in-tree-views-2814#attr=21700 That also didn't work but I think it's because the module is for version 15 and I have 16.

Does anyone have an idea for the solution?

2

There are 2 best solutions below

0
On

Have you properly added the path to your js-file in the assets entry of your __manifest__.py file ?

    
    # ...
    'assets': {
        'web.assets_frontend': [     
            '/your_module/static/src/js/your_jsfile.js',       

        ],
    },
    # ...
   

Where does your record.data.color come from ?

using $tr.addClass('your_class_color_red') , that needs to be defined in css : .your_class_color_red{ background-color: red !important;} ... would match better the original code than your record.data.color

The original js file : /addons/web/static/src/legacy/js/views/list/list_renderer.js

/** @odoo-module alias=web.ListRenderer **/

import BasicRenderer from 'web.BasicRenderer';
import { ComponentWrapper } from 'web.OwlCompatibility';
import config from 'web.config';
import core from 'web.core';
import dom from 'web.dom';
import field_utils from 'web.field_utils';
import Pager from 'web.Pager';
import utils from 'web.utils';
import viewUtils from 'web.viewUtils';

var _t = core._t;

// Allowed decoration on the list's rows: bold, italic and bootstrap semantics classes
var DECORATIONS = [
    'decoration-bf',
    'decoration-it',
    'decoration-danger',
    'decoration-info',
    'decoration-muted',
    'decoration-primary',
    'decoration-success',
    'decoration-warning'
];

var FIELD_CLASSES = {
    char: 'o_list_char',
    float: 'o_list_number',
    integer: 'o_list_number',
    monetary: 'o_list_number',
    text: 'o_list_text',
    many2one: 'o_list_many2one',
};

var ListRenderer = BasicRenderer.extend({
    className: 'o_legacy_list_view',
    events: {
        "mousedown": "_onMouseDown",
        "click .o_optional_columns_dropdown .dropdown-item": "_onToggleOptionalColumn",
        "click .o_optional_columns_dropdown_toggle": "_onToggleOptionalColumnDropdown",
        'click tbody tr': '_onRowClicked',
        'change tbody .o_list_record_selector': '_onSelectRecord',
        'click thead th.o_column_sortable': '_onSortColumn',
        'click .o_list_record_selector': '_onToggleCheckbox',
        'click .o_group_header': '_onToggleGroup',
        'change thead .o_list_record_selector input': '_onToggleSelection',
        'keypress thead tr td': '_onKeyPress',
        'keydown td': '_onKeyDown',
        'keydown th': '_onKeyDown',
    },
    sampleDataTargets: [
        '.o_data_row',
        '.o_group_header',
        '.o_list_table > tfoot',
        '.o_list_table > thead .o_list_record_selector',
    ],
    /**
     * @constructor
     * @param {Widget} parent
     * @param {any} state
     * @param {Object} params
     * @param {boolean} params.hasSelectors
     */
    init: function (parent, state, params) {
        this._super.apply(this, arguments);
        this._preprocessColumns();
        this.columnInvisibleFields = params.columnInvisibleFields || {};
        this.rowDecorations = this._extractDecorationAttrs(this.arch);
        this.fieldDecorations = {};
        for (const field of this.arch.children.filter(c => c.tag === 'field')) {
            const decorations = this._extractDecorationAttrs(field);
            this.fieldDecorations[field.attrs.name] = decorations;
        }
        this.hasSelectors = params.hasSelectors;
        this.selection = params.selectedRecords || [];
        this.pagers = []; // instantiated pagers (only for grouped lists)
        this.isGrouped = this.state.groupedBy.length > 0;
        this.groupbys = params.groupbys;
        this.no_open = params.no_open;
    },
//...
//...
/**
     * Render a row, corresponding to a record.
     *
     * @private
     * @param {Object} record
     * @returns {jQueryElement} a <tr> element
     */
    _renderRow: function (record) {
        var self = this;
        var $cells = this.columns.map(function (node, index) {
            return self._renderBodyCell(record, node, index, { mode: 'readonly' });
        });

        var $tr = $('<tr/>', { class: 'o_data_row' })
            .attr('data-id', record.id)
            .append($cells);
        if (this.hasSelectors) {
            $tr.prepend(this._renderSelector('td', !record.res_id));
        }
        if (this.no_open && this.mode === "readonly") {
            $tr.addClass('o_list_no_open');
        }
        this._setDecorationClasses($tr, this.rowDecorations, record);
        return $tr;
    },
//...
//...

An example of inheritance existing as standard in v16 (/addons/web/static/src/legacy/js/views/list/list_editable_renderer.js):


/** @odoo-module alias=web.EditableListRenderer **/

/**
 * Editable List renderer
 *
 * The list renderer is reasonably complex, so we split it in two files. This
 * file simply 'includes' the basic ListRenderer to add all the necessary
 * behaviors to enable editing records.
 *
 * Unlike Odoo v10 and before, this list renderer is independant from the form
 * view. It uses the same widgets, but the code is totally stand alone.
 */
import core from 'web.core';
import dom from 'web.dom';
import ListRenderer from 'web.ListRenderer';
import utils from 'web.utils';
import { WidgetAdapterMixin } from 'web.OwlCompatibility';

var _t = core._t;

ListRenderer.include({
    RESIZE_DELAY: 200,
    custom_events: _.extend({}, ListRenderer.prototype.custom_events, {
        navigation_move: '_onNavigationMove',
    }),
    events: _.extend({}, ListRenderer.prototype.events, {
        'click .o_field_x2many_list_row_add a': '_onAddRecord',
        'click .o_group_field_row_add a': '_onAddRecordToGroup',
        'keydown .o_field_x2many_list_row_add a': '_onKeyDownAddRecord',
        'click tbody td.o_data_cell': '_onCellClick',
        'click tbody tr:not(.o_data_row)': '_onEmptyRowClick',
        'click tfoot': '_onFooterClick',
        'click tr .o_list_record_remove': '_onRemoveIconClick',
    }),
    /**
     * @override
     * @param {Object} params
     * @param {boolean} params.addCreateLine
     * @param {boolean} params.addCreateLineInGroups
     * @param {boolean} params.addTrashIcon
     * @param {boolean} params.isMany2Many
     * @param {boolean} params.isMultiEditable
     */
    init: function (parent, state, params) {
        this._super.apply(this, arguments);

        this.editable = params.editable;
        this.isMultiEditable = params.isMultiEditable;
        this.columnWidths = false;

        // if addCreateLine (resp. addCreateLineInGroups) is true, the renderer
        // will add a 'Add a line' link at the bottom of the list view (resp.
        // at the bottom of each group)
        this.addCreateLine = params.addCreateLine;
        this.addCreateLineInGroups = params.addCreateLineInGroups;

//...
//...

    _renderRow: function (record, index) {
        var $row = this._super.apply(this, arguments);
        if (this.addTrashIcon) {
            var $icon = this.isMany2Many ?
                $('<button>', {'class': 'fa fa-times', 'name': 'unlink', 'aria-label': _t('Unlink row ') + (index + 1)}) :
                $('<button>', {'class': 'fa fa-trash-o', 'name': 'delete', 'aria-label': _t('Delete row ') + (index + 1)});
            var $td = $('<td>', {class: 'o_list_record_remove'}).append($icon);
            $row.append($td);
        }
        return $row;
    },
0
On

You can override the getRowClass function of the list renderer, compute class names using record values and an expression passed through the action's context

In the following example we create a custom list view (to use it, set js_class on the tree tag):

Example:

/** @odoo-module **/

import { registry } from '@web/core/registry';
import { ListRenderer } from "@web/views/list/list_renderer";
import { listView } from "@web/views/list/list_view";
var py = window.py;


export class ListRendererRowColors extends ListRenderer {

    getRowClass(record) {
        // classnames coming from decorations
        const classNames = super.getRowClass(record).split(" ");
        if(this.list.context.line_color) {
            var colorExpr = this.list.context.line_color.split(':');
            var rowClassNames = colorExpr[0].trim().split(" ");
            var expr = colorExpr[1].trim();
            if(py.eval(expr, record.evalContext)) {
                classNames.push(...rowClassNames);
            }
        }

        return classNames.join(" ");
    }
};

registry.category('views').add('row_color_tree', {
    ...listView,
    Renderer: ListRendererRowColors,
});

Then you can use the following expression in the context :

{'line_color': 'cls1 cls2:is_line_nonsense == True'}