UI table row color

Hi all.

I am newbie in node red. I have som little experiences, but not much.So I have read many examples and also imported example ui control of table, but I am not able to create simple function, that highlight a row by red color in the table, that has a value send via flow.set <10.
UI table is readed from DB directly. Here is code. If you can help me, I will be glad. Many thanks for your time.

[
    {
        "id": "13861ca69f2c20b6",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "79f18799d32c03d1",
        "type": "ui_table",
        "z": "13861ca69f2c20b6",
        "group": "ec06941b4e2e607f",
        "name": "",
        "order": 8,
        "width": "25",
        "height": "12",
        "columns": [
            {
                "field": "id",
                "title": "<small>ID</small>",
                "width": "3%",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "location",
                "title": "<small>Umiestnenie</small>",
                "width": "12%",
                "align": "left",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "input_rel",
                "title": "<small>Zdroj.relé</small>",
                "width": "",
                "align": "center",
                "formatter": "tickCross",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "charge_rel",
                "title": "<small>Nabíj.relé</small>",
                "width": "",
                "align": "center",
                "formatter": "tickCross",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "output_rel",
                "title": "<small>Výst.relé</small>",
                "width": "",
                "align": "center",
                "formatter": "tickCross",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "output_rel_time",
                "title": "<small>ÄŚas do zap.rel.</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "batt_volt",
                "title": "<small>U bat.(V)</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "charge_volt",
                "title": "<small>U nab.(V)</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "input_volt",
                "title": "<small>U zdroja(V)</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "charge_curr",
                "title": "<small>I nab.(A)</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "output_curr",
                "title": "<small>I výst.(A)</small>",
                "width": "",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "charge_status",
                "title": "<small>Stav nab.</small>",
                "width": "",
                "align": "center",
                "formatter": "traffic",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "output_power",
                "title": "<small>P-(W)</small>",
                "width": "6%",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "temp_box",
                "title": "<small>T(°C)</small>",
                "width": "5%",
                "align": "center",
                "formatter": "plaintext",
                "formatterParams": {
                    "target": "_blank"
                }
            },
            {
                "field": "errors_mess",
                "title": "<small>Info/Err</small>",
                "width": "6%",
                "align": "left",
                "formatter": "color",
                "formatterParams": {
                    "target": "_blank"
                }
            }
        ],
        "outputs": 1,
        "cts": true,
        "x": 470,
        "y": 100,
        "wires": [
            [
                "01fac031c65184bc"
            ]
        ]
    },
    {
        "id": "54f34611329ab63f",
        "type": "sqlite",
        "z": "13861ca69f2c20b6",
        "mydb": "77663dac4f64af8e",
        "sqlquery": "msg.topic",
        "sql": "",
        "name": "sqlitedb",
        "x": 300,
        "y": 100,
        "wires": [
            [
                "79f18799d32c03d1"
            ]
        ]
    },
    {
        "id": "6e7912c9adec9a32",
        "type": "inject",
        "z": "13861ca69f2c20b6",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": true,
        "onceDelay": "2",
        "topic": "SELECT * FROM ups_data ORDER BY id;",
        "payload": "",
        "payloadType": "date",
        "x": 130,
        "y": 100,
        "wires": [
            [
                "54f34611329ab63f"
            ]
        ]
    },
    {
        "id": "f6aedf5c5e1a93f0",
        "type": "ui_toast",
        "z": "13861ca69f2c20b6",
        "position": "dialog",
        "displayTime": "3",
        "highlight": "",
        "sendall": false,
        "outputs": 1,
        "ok": "OK",
        "cancel": "",
        "raw": true,
        "className": "",
        "topic": "",
        "name": "info_panel",
        "x": 690,
        "y": 200,
        "wires": [
            []
        ]
    },
    {
        "id": "01fac031c65184bc",
        "type": "function",
        "z": "13861ca69f2c20b6",
        "name": "function 22",
        "func": "var msg1, msg2, msg3, errs;\nvar sel_row = flow.get(\"db_row_id\");// ID autoincremented by DB\nvar data_extr = msg.payload; // extracted data from payload\nvar info_loc, info_text, info_err;\ninfo_loc = msg.payload.location; // location info from DB\nif (msg.payload.errors_num==0){\nerrs = \"Without err\";       \n} else if (msg.payload.errors_num == 1){\n    errs = \"Battery charging error.\";     \n} else if (msg.payload.errors_num == 2) {\n    errs = \"Low battery capacity.\";\n} else if (msg.payload.errors_num == 3) {\n    errs = \"Battery discarged.\";\n} else if (msg.payload.errors_num == 4) {\n    errs = \"Battery not connected.\";\n} else if (msg.payload.errors_num == 5) {\n    errs = \"Longest duration of battery capacity !\";\n} else if (msg.payload.errors_num == 6) {\n    errs = \"Battery test running\";\n}\ninfo_text = \"<b>IP adr :</b> \" + msg.payload.ris_ip + \"<br><b>ASDU(ID) :</b> \" + msg.payload.ris_id + \"<br><b>Messages :</b> \" + errs;\nmsg1 = { topic: info_loc, payload: info_text};\nmsg1.socketid = msg.socketid;\nreturn [msg1, msg2, msg3];",
        "outputs": 3,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 110,
        "wires": [
            [
                "f6aedf5c5e1a93f0"
            ],
            [],
            []
        ]
    },
    {
        "id": "391dfd00f6a932dd",
        "type": "link in",
        "z": "13861ca69f2c20b6",
        "name": "link in 1",
        "links": [
            "4555861d9e04dff6"
        ],
        "x": 275,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "ec06941b4e2e607f",
        "type": "ui_group",
        "name": "Dáta UPS",
        "tab": "8971efe7.4a1af",
        "order": 1,
        "disp": true,
        "width": "25",
        "collapse": false,
        "className": ""
    },
    {
        "id": "77663dac4f64af8e",
        "type": "sqlitedb",
        "db": "/home/pi/SmartUPS/db_data_ups.sqlite",
        "mode": "RW"
    },
    {
        "id": "8971efe7.4a1af",
        "type": "ui_tab",
        "name": "Main view",
        "icon": "fa-bar-chart",
        "order": 1,
        "disabled": false,
        "hidden": false
    }
]

I understand that you are using the table node of dashboard-1. You say you are a newbie - why not start with dashboard-2? (dashboard-1 is no longer supported, and specifically, its table node does not support custom formatting functions).

In dashboard-2, you have 2 options:

  1. Use the basic table node, which is based on Vue. It gives you basic functionality, and you can set conditional formatting by providing a row-props function in your data area which returns a CSS class/style based on the row data.

  2. Alternatively, you can use the much more powerful @omrid/node-red-dashboard-2-table-tabulator node , which is based on the same engine as in D-1 (Tabulator), and fully supports internal functions and conditional formatting (see the on-line help and provided examples).

Thanks Omrid for your help. Your package is not possible to install at me, wrotes me :

So there is an error : Unsuported engine.
Except it, your first select is possible. But I do not found any row props and how is possible to applicate to my code. At this moment I have installed DB2 from flowfuse. Also I have connected it to DB and its nice, but without a row-props specifications.

'unsupported engine' typically relates to NPM version compatibility.
Try this:

  • Open a command-line terminal (with admin rights)
  • Go to your Node-red home directory, e.g. \Users<username>.node-red (Windows) or ~/.node-red (Mac or Linux)
  • type NPM install @omrid/node-red-dashboard-2-table-tabulator

You are running an old version of node.js. Which also means that you are likely running an old version of Node-RED?

Please update to the current version of Node-RED but you will first need to update Node.js to at least v18.20 - v22 LTS is the current recommended version.

Once you are able to install ui-tabulator, I would highly recommend importing the sample flows that @omrid has created. Those are a good starting point to see how to configure, populate and interact with ui-tablulator.

Here is the link to the examples (on GitHub) ui-tabulator sample flows

Note that, for every node which includes examples, you can import them directly from the 'import' dialog

1 Like

Oh yes! Completely forgot about that :man_facepalming:t2:

I have a nodered v.3.0.2 and node.js version 16.20.2

OK, now I have updated it to nr 4.1 and node.js 22. At this moment I have installed Omri’s package of tabulator. Example looks nice.
If I return to my first post, how do I add to function node this settings and how can I identify a row number, which must be colored based on input value set by flow.set ?
In PHP is not problem for me, but I am not professional in json and javascripts. Thanks to help.

I can't help with that I'm afraid as I don't really use Dashboard 2 or the tabulator node for it. (I am the author of UIBUILDER).

However, I think that there may have been some recent posts related to tabulator node and that may help. Also check for any examples (ctrl+i to get the import dialog, check the examples and look for the tabulator node).

Oh, I understand. May be @omrid could help me. So, big thanks to right direction, that help me solved part of problems.

OK, here's an example. In ui-tabulator, let's assume you have a column "age", and you want red background color for every row in which age<18.

  1. In the table configuration, add a row formatter, which will dynamically set the row background color to red if age<18. The formatter will be called ageColor, and will be prefixed by @F: to indicate that it is a function.
{
   "height": 300,
   "columns": [...],
   "data": [...],
   "rowFormatter":"@F:ageColor"
}
  1. Now define the actual function body in the Custom Functions area:
function ageColor (row)
{
    if (row.getData().age < 18)
       row.getElement().style.backgroundColor = "red";
}

That's it. Just change it to your required logic.

1 Like

Thanks for help me. So I am trying it at this moment.
I took one else problem. My nodes looks like this :

Function node has a code :

msg.tbArgs = [ [	 		{ id: msg.payload.id, location: msg.payload.location, errors_num: msg.payload.errors_num}, 	] ] return msg; 

Tabulator node has a code :

{
   "height": 200,
   "selectableRows": true,
   "layout": "fitColumns",
   "data": [
    "msg.payload" 
   ],
   "columns": [
      {
         "title": "Id",
         "field": "id",
         "width": "3%",
         "headerSortTristate": true,
         "hozAlign": "center"
      },
      {
         "title": "Location",
         "field": "location",
         "width": "10%",
         "headerSortTristate": true,
         "hozAlign": "left",
         "headerFilter": "input",
         "headerFilterPlaceholder": "Filter..."
      },
      {
         "title": "ASDU add.",
         "field": "ris_id",
         "width": "5%",
         "headerSortTristate": true,
         "hozAlign": "left",
         "headerFilter": "input",
         "headerFilterPlaceholder": "Filter..."
      }
      
   ]
}

So I think, that is not correct, but definition this is for me too difficult, than I will understand. Thanks for your time.

Bohumil

I did it. Wau.

Function node code :

msg.tbCmd = "setData";
msg.tbArgs = [
msg.payload
]
return msg;

Tabulator node :

{
	"height": 200,
	"selectableRows": false,
    "layout":"fitColumns",
	"data": [
		"msg.tbArgs"		
	],
	"columns": [
		{
			"title": "Id",
			"field": "id",
			"width": "5%",
			"headerSortTristate":false,
			"hozAlign": "center"
		},
		{
			"title": "Location",
			"field": "location",
			"width": "15%",
			"headerSortTristate":false,
			"hozAlign": "left",
			"headerFilter": "input",
			"headerFilterPlaceholder": "Filter..."
		},
		{
			"title": "Messages",
			"field": "errors_num",
			"width": "8%",
			"headerSortTristate":false,
			"hozAlign": "left",
			"formatter": "@F:errColor",
			"formatterParams": {
				"minErr": 1
			}
		
		},
		{
			"title": "Inp.relay",
			"field": "input_rel",
			"width": "5%",
			"headerSortTristate": false,
			"hozAlign": "center",
			"formatter": "traffic",
			"formatterParams": {
				"min": 0,
				"max": 1,
				"color": [
					"red",
					"green"
				]
			}
		},
		{
			"title": "Chrg.relay",
			"field": "charge_rel",
			"width": "5%",
			"headerSortTristate": false,
			"hozAlign": "center",
			"formatter": "traffic",
			"formatterParams": {
				"min": 0,
				"max": 1,
				"color": [
					"red",
					"green"
				]
			}
		},
		{
			"title": "Out relay",
			"field": "output_rel",
			"width": "5%",
			"headerSortTristate": false,
			"hozAlign": "center",
			"formatter": "traffic",
			"formatterParams": {
				"min": 0,
				"max": 1,
				"color": [
					"red",
					"green"
				]
			}
		}
	]
}

And Custom functions :

function errColor(cell, params) {
    // Conditional formatter
    if (cell.getValue() < params.minErr){
   cell.getElement().style.color = 'green';     
    }else{
cell.getElement().style.color = 'red';
    }
        
    return cell.getValue();
}

So I am happy and big thanks @omrid and all colleagues.

If your table definition is in the ui-tabular configuration, I don’t think you need to to specify this. In my setup, I have the table definition and mapping col/field in the ui_tabulator configuration and I just send in msg.tbCmd=”setData” and msg.tbArgs=msg.payload from a change node.

@rakgupta is correct. The node's table configuration is applied when the node is created - before any message is sent or received, so data: [msg.tbArgs] is undefined and ignored. Just remove it.

Thanks to both @omrid and @rakgupta for clearing me this process. I am in study mode and all informations about this type of dashboard and its nodes are really new for me.