Examples for node-red-node-ui-table

It actually uses cellClick but never mind.

Yes it is. You can use msg.ui_control send other callbacks to tabulator or even take control over the complete layout. See some posts before :wink: Ui-table supports ui_control

I use this to do the complete setup of my tables.

A little tip: I use a change node emitting a json object (or most of the time a sub flow with a json environment variable) to configure ui-table and do other things. I write the callback functions in vs-code (but the editor of a function node is fine too). Then I use the visual json editor to paste in the function. All escape sequencing of " to \" is done automatically

    "tabulator": {
        "responsiveLayout": "collapse",
        "responsiveLayoutCollapseStartOpen": false,
        "index": "$name",
        "layout": "fitColumns",
        "movableColumns": true,
        "groupBy": "",
        "columnResized": "function(column){     var newColumn = {         field: column._column.field,         visible: column._column.visible,         width: column._column.width,         widthFixed: column._column.widthFixed,         widthStyled: column._column.widthStyled     }; this.send({topic:this.config.topic,ui_control:{callback:'columnResized',columnWidths:newColumn}}); }",
        "columnMoved": "function(column, columns){     var newColumns=[];     columns.forEach(function (column) {         newColumns.push({'field': column._column.definition.field, 'title': column._column.definition.title});     });     this.send({topic:this.config.topic,ui_control:{callback:'columnMoved',columns:newColumns}}); }",
        "rowFormatter": "function(row){     var data = row.getData();     switch (data.$state) {         case \"lost\":             row.getElement().style.backgroundColor = \"#9e2e66\";             row.getElement().style.color = \"#a6a6a6\";             break;         case \"sleeping\":             row.getElement().style.backgroundColor = \"#336699\";             break;         case \"disconnected\":             row.getElement().style.backgroundColor = \"#cc3300\";             row.getElement().style.color = \"#a6a6a6\";             break;         case \"alert\":             row.getElement().style.backgroundColor = \"#A6A6DF\";             break;         case \"init\":             row.getElement().style.backgroundColor = \"#f2f20d\";             break;         case \"ready\":             row.getElement().style.backgroundColor = \"\";             row.getElement().style.color = \"\";             break;         } }",
        "columns": [
            {
                "formatter": "responsiveCollapse",
                "width": 30,
                "minWidth": 30,
                "align": "center",
                "resizable": false,
                "headerSort": false,
                "frozen": true,
                "title": "expand",
                "field": "expand",
                "headerVertical": "flip"
            },
            {
                "formatter": "function(cell, formatterParams, onRendered) {      var html = cell.getValue(); return html;  }",
                "title": "State",
                "field": "$stateIcon",
                "width": 100,
                "frozen": true,
                "headerVertical": "flip"
            },
´´´
--- cutted here ---

Once again thanks for the tip. It was the weird syntax that got me, as usual :slight_smile:

1 Like

@T0T4R4 can you share your flow?

Hi Guys, New to node-red.
Is it possible to have an example of replaceData commands to ui-table.

I'd like to to update a particular cell in table.

Many Thanks.

Best is to use the addOrUpdate command (take a look at the example ui-table and commands menu/import/examples/ui-table)

Now I`m back on my computer and can give you a litle bit more info

msgToTable.payload={
    "command":"updateOrAddData",
    "arguments": [[{"id":123,"columnField":"updated"},{"id":124,"columnField":"new"}]],
    "returnPromise": false
};

To update a single cell (or many cells in a row) the row has to be identified. By default the field id is defined for that purpose but it can be changed by "index":"anyField". If the value of the index property isn't in the table a new row will be added (if id 124 is not present).

Just came across this new table plugin code. is it better to use it instead of Tabulator ? It is new repo and certainly looks like lightweight ,simpler and promising.


Thanks.

Depends what you try to achieve. Quickly browsed through the examples

  • no formatters
  • no cell edits
  • no filters
  • no freezed columns
  • no custom callbacks
  • no drag/drop
  • no copy/paste
  • no column or row sorting / rearranging
  • no reaktive layout
  • no child tables / columns or rows
  • ...

Certainly very lightweight in resources but at the same time in functionality. Very jung - not a bad thing but.,.

Both are pure JavaScript and framework independent. Think not difficult to do a warper for it but in my case not attractive

What is wrong with tabulator? Really curious to know.

Tabulator little too compicated for me. Although it works for me after following your example.

Thanks.

Has anyone of you used the editable feature or any idea how to implement this?

Using the editable setting on each column, you can make a user editable table. Any time a cell is edited it triggers the cellEdited callback, to allow you to process any changes.

Yes ... isn't too difficult. You need a callback function to send the edited data back to Node-RED. Best is to define one for the hole table independent of the type of editor defined for the column:

msg.ui_control.tabulator.cellEdited.

function(cell){     
    this.send({
        ui_control:{callback:'cellEdited'},         
        payload:cell.getValue(),
        oldValue:cell.getOldValue(),
        field:cell.getColumn().getField(),
        id:cell.getRow().getCell('id').getValue()
    }); 
}

Then you only have to define the editor type for every column you want to have the edit feature enabled like msg.ui_control.tabulator.colums[{field:'yourColum',editor:'input'}]

If you need some kind on validation you perhaps write individual cellEdited functions for each column.

ui-table node will send a message for every edited field containing the row (id or any other index), the column (field), new and old value.

3 Likes

that sounds exactly like what I'm looking for! By any chance, do you have this as a sample flow? I have not yet worked with the tabulator feature and am not 100% sure where to integrate this.

Thanks!

Thanks again for your hint, I managed to adapt one of the supplied examples:

now you can click into the column "ROom" and change the text. The change is then emitted on the output-port of the table.

[{"id":"c4f39e9c.3642f8","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"455415d1.6b50b4","type":"inject","z":"c4f39e9c.3642f8","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":440,"y":320,"wires":[["dd154dab.fcd418"]]},{"id":"59a0cfff.9bcb","type":"debug","z":"c4f39e9c.3642f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":910,"y":320,"wires":[]},{"id":"4c5c306f.e02298","type":"ui_table","z":"c4f39e9c.3642f8","group":"b070d416.db371","name":"Thermostats","order":1,"width":"14","height":"7","columns":[],"outputs":1,"cts":true,"x":750,"y":320,"wires":[["59a0cfff.9bcb"]]},{"id":"dd154dab.fcd418","type":"function","z":"c4f39e9c.3642f8","name":"","func":"msg.payload = [\n    {\"name\":\"MEQ0451495\",\"room\":\"Bathroom\",\"SET_TEMPERATURE-value\":22,\"ACTUAL_TEMPERATURE-value\":21.8,\"VALVE_STATE-value\":90,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":true,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ1875547\",\"room\":\"Living Room\",\"SET_TEMPERATURE-value\":12,\"ACTUAL_TEMPERATURE-value\":16.2,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":1},\n    {\"name\":\"MEQ1875538\",\"room\":\"Living Room\",\"SET_TEMPERATURE-value\":18,\"ACTUAL_TEMPERATURE-value\":19.5,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.6,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":2},\n    {\"name\":\"MEQ0447462\",\"room\":\"Kitchen\",\"SET_TEMPERATURE-value\":17,\"ACTUAL_TEMPERATURE-value\":22.2,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":10,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":3},\n    {\"name\":\"MEQ1875551\",\"room\":\"Office\",\"SET_TEMPERATURE-value\":18,\"ACTUAL_TEMPERATURE-value\":20.2,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ0447425\",\"room\":\"Dining Room\",\"SET_TEMPERATURE-value\":19,\"ACTUAL_TEMPERATURE-value\":20.4,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ1875546\",\"room\":\"Dining Room\",\"SET_TEMPERATURE-value\":20,\"ACTUAL_TEMPERATURE-value\":18.8,\"VALVE_STATE-value\":99,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ0447483\",\"room\":\"Bedroom\",\"SET_TEMPERATURE-value\":17,\"ACTUAL_TEMPERATURE-value\":22.4,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ1875541\",\"room\":\"Child\",\"SET_TEMPERATURE-value\":18,\"ACTUAL_TEMPERATURE-value\":20.4,\"VALVE_STATE-value\":0,\"BATTERY_STATE-value\":2.7,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0},\n    {\"name\":\"MEQ1875552\",\"room\":\"Guest Room\",\"SET_TEMPERATURE-value\":20,\"ACTUAL_TEMPERATURE-value\":21.1,\"VALVE_STATE-value\":9,\"BATTERY_STATE-value\":2.8,\"BOOST_STATE-value\":0,\"AUTO_MODE-value\":false,\"CONTROL_MODE-value\":0}\n]\n\nmsg.ui_control =  {\n    \"tabulator\":{\n        \"columnResized\":\"function(column){var newColumn = {         field: column._column.field,         visible: column._column.visible,         width: column._column.width,         widthFixed: column._column.widthFixed,         widthStyled: column._column.widthStyled     }; this.send({topic:this.config.topic,ui_control:{callback:'columnResized',columnWidths:newColumn}}); }\",\n        \"columnMoved\":\"function(column, columns){     var newColumns=[];     columns.forEach(function (column) {         newColumns.push({'field': column._column.field});     });     this.send({topic:this.config.topic,ui_control:{callback:'columnMoved',columns:newColumns}}); }\",\n        \"groupHeader\":\"function (value, count, data, group) {return value + \\\"<span style='color:#d00; margin-left:10px;'>(\\\" + count + \\\" Termostat\\\"+((count>1) ? \\\"e\\\" : \\\"\\\") + \\\")</span>\\\";}\",\n        \"columns\":[\n            {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"ROom\",\"field\":\"room\",\"width\":100, \"editor\":\"input\"},\n            {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Device\",\"field\":\"name\",\"width\":100,\"align\":\"center\"},\n            {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Type\",\"field\":\"deviceType\",\"width\":100,\"align\":\"center\"},\n            {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Measurements\",\"columns\":[\n                {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"target\",\"field\":\"SET_TEMPERATURE-value\",\"formatter\":\"function(cell, formatterParams, onRendered){return cell.getValue()+'°C';}\",\"topCalc\":\"avg\",\"width\":100},\n                {\"formatterParams\":{\"target\":\"_blank\",\"min\":10,\"max\":25,\"color\":[\"blue\",\"green\",\"red\"],\"legend\":\"function (value) {return '&nbsp;&nbsp;'+value+'°C';}\",\"legendColor\":\"#101010\",\"legendAlign\":\"left\"},\"title\":\"current\",\"field\":\"ACTUAL_TEMPERATURE-value\",\"formatter\":\"progress\",\"topCalc\":\"avg\",\"width\":100},\n                {\"formatterParams\":{\"target\":\"_blank\",\"min\":0,\"max\":99,\"color\":[\"gray\",\"orange\",\"red\"],\"legend\":\"function (value) {return (value>0)? '&nbsp;&nbsp;'+value+' %' : '-';}\",\"legendColor\":\"#101010\",\"legendAlign\":\"center\"},\"title\":\"Valve\",\"field\":\"VALVE_STATE-value\",\"formatter\":\"progress\",\"topCalc\":\"max\",\"width\":100},\n                {\"formatterParams\":{\"target\":\"_blank\",\"min\":1.5,\"max\":4.6,\"color\":[\"red\",\"orange\",\"green\"],\"legend\":\"function (value) {return value+' V';}\",\"legendColor\":\"#101010\",\"legendAlign\":\"center\"},\"title\":\"Batt\",\"field\":\"BATTERY_STATE-value\",\"formatter\":\"progress\",\"topCalc\":\"min\",\"width\":100}]},\n                {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Settings\",\"columns\":[\n                {\"formatterParams\":{\"target\":\"_blank\",\"min\":0,\"max\":30,\"color\":[\"red\",\"orange\",\"green\"],\"legend\":\"function (value) {     if (value>0)         return \\\"<span style='color:#101010;'>\\\"+value+\\\" min</span>\\\";     else         return \\\"<span style='color:#A0A0A0;'>aus</span>\\\"; }\",\"legendColor\":\"#101010\",\"legendAlign\":\"center\"},\"title\":\"Boost\",\"field\":\"BOOST_STATE-value\",\"formatter\":\"progress\",\"width\":100},\n                {\"formatterParams\":{\"target\":\"_blank\",\"allowEmpty\":true,\"allowTruthy\":true,\"tickElement\":\"<i class='fa fa-clock-o'></i>\",\"crossElement\":\"<i class='fa fa-ban'></i>\"},\"title\":\"Auto\",\"field\":\"AUTO_MODE-value\",\"formatter\":\"tickCross\",\"width\":100,\"align\":\"center\"},\n                {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Mode\",\"field\":\"CONTROL_MODE-value\",\"formatter\":\"function(cell, formatterParams, onRendered){     var html=\\\"<i class=\\\\\\\"\\\";     switch(cell.getValue()) {         case 0: html+=\\\"fa fa-calendar-check-o\\\"; break;         case 1: html+=\\\"fa fa-hand-o-up\\\"; break;         case 2: html+=\\\"fa fa-suitcase\\\"; break;         case 3: html+=\\\"fa fa-spinner fa-spin fa-fw\\\"; break;     }     html+='\\\\\\\"></i>';     return html; }\",\"width\":100,\"align\":\"center\"},\n                {\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Auto\",\"field\":\"AUTO_MODE-value\",\"formatter\":\"tick\",\"width\":100,\"align\":\"center\"}\n                ]\n            }\n        ],\n        //\"cellEdited\":\"function(cell){this.send({ui_control:{callback:'cellEdited'},payload:cell.getValue(),oldValue:cell.getOldValue(),field:cell.getColumn().getField(),id:cell.getRow().getCell('id').getValue()}); }\",\n        \"cellEdited\":\"function(cell){var change = {newValue:cell.getValue()};this.send({topic:this.config.topic,ui_control:{callback:'cellEdited',changes:change}});}\",\n        \"layout\":\"fitColumns\",\n        \"movableColumns\":true,\n        \"groupBy\":\"\"\n    },\n    \"customHeight\":12\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":320,"wires":[["4c5c306f.e02298"]]},{"id":"cf058e1a.c30368","type":"inject","z":"c4f39e9c.3642f8","name":"reset table","topic":"","payload":"[]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":580,"y":360,"wires":[["4c5c306f.e02298"]]},{"id":"b070d416.db371","type":"ui_group","z":"","name":"Table","tab":"cd895ba5.a7168","disp":true,"width":"14","collapse":false},{"id":"cd895ba5.a7168","type":"ui_tab","z":"","name":"newTable","icon":"dashboard","disabled":false,"hidden":false}]
4 Likes

Great you found your own solution and my typo :crazy_face:

Another issue I came across: according to the tabulator doc's, it should be possible to change the color of a row depending on some column's value:

msg.payload = [{"time": 1, "value":true}, {"time":2, "value": false}, {"time":3, "value": true}];

columns = [
{   
        title:"Time",       
        field:"time",      
        width:190,      
        align:"left", 
        headerSortStartingDir:"desc"
    },
    {
        title:"Status",            
        field:"value",          
        width:250,       
        align:"center", 
        headerSort:false
}]

msg.ui_control = {};
msg.ui_control.tabulator = {
    "placeholder":"empty",
    "columns":columns,
    "pagination":"local",
    "paginationSize":20,
    "initialSort":[
        {column:"timestamp", dir:"desc"}
    ],
    "rowFormatter":function(row){
        var data = row.getData();

        if(data.value === true){
            row.getElement().style.backgroundColor = "#A6A6DF";
            row.getElement().style.color= "#FF0000";
        } 
    }
}

Unfortunately, this does not work for me - anyone who got this to work?

1 Like

Yes ... I use this function in my "remote device table"

image

to indicate the state of my remote devices.

{
    "tabulator": {
        "rowFormatter": "function(row){     var data = row.getData();     switch (data.$state) {         case \"lost\":             row.getElement().style.backgroundColor = \"#9e2e66\";             row.getElement().style.color = \"#a6a6a6\";             break;         case \"sleeping\":             row.getElement().style.backgroundColor = \"#336699\";             break;         case \"disconnected\":             row.getElement().style.backgroundColor = \"#cc3300\";             row.getElement().style.color = \"#a6a6a6\";             break;         case \"alert\":             row.getElement().style.backgroundColor = \"#A6A6DF\";             break;         case \"init\":             row.getElement().style.backgroundColor = \"#f2f20d\";             break;         case \"ready\":             row.getElement().style.backgroundColor = \"\";             row.getElement().style.color = \"\";             break;         } }",
    }
}
rowFormatter = function(row){
    var data = row.getData();
    switch (data.$state) {
        case "lost":
            row.getElement().style.backgroundColor = "#9e2e66";
            row.getElement().style.color = "#a6a6a6";
            break;
        case "sleeping":             
            row.getElement().style.backgroundColor = "#336699";             
            break;         
        case "disconnected":             
            row.getElement().style.backgroundColor = "#cc3300";             
            row.getElement().style.color = "#a6a6a6";             
            break;         
        case "alert":             
            row.getElement().style.backgroundColor = "#A6A6DF";             
            break;         
        case "init":             
            row.getElement().style.backgroundColor = "#f2f20d";             
            break;         
        case "ready":             
            row.getElement().style.backgroundColor = "";             
            row.getElement().style.color = "";             
            break;         
    }
}

It could be that your boolean values are not stored as boolean type. I would insert a debugger statement behind the row.getData() call and check what value and type data.value actually is. (I love the "beauty" of javascript)

3 Likes

Hi
Is it possible to add a text field to search for lines containing the searched word? is it possible to see an example?

1 Like

Yes it is possible by using filters. Easiest are header filters:

This is used in the syslog table:

A little bit complex but you should find the relevant parameters in the tabulator json.

But you can theoretically control filters by sending commands and using dashboard input widget or other sources.

Hi,

I looked into your flow.

First, I need some demo data. Please put a debug node behind your csv node and copy the data from the debug tab. Put this data as a json into a inject node.

Second in which column are you searching for your input data? Like Excel you can only filter in columns individually. (a "full text" search is not available in tabulator out of the box (as far as I know))

Third, please take a look into the ui_control example to define your table by using ui_control messages. This will give you the ability to use the header filters.

Here you find an example to inject demo data too.

Thank you