Odoo Color one2many Treeview Line

298 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 answers

0
Ahrimann 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
Kenly 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'}