Ui-table supports ui_control

Hi
I am midway through constructing a table for node red heating and am stuck on one thing. I need to get back into node red the list of selected rows in the table.
I have added the selector which works as i expect- see below. Clicking on any cell in the table apart from the selector row returns info to node red. I'm guessing i need to add something in the

addsomethingHere() bit?

I am at the 'copy and fiddle around' html coding standard!

Can anyone help please?

        "columns": [
            {
                "formatter": "rowSelection",
                "titleFormatter": "rowSelection",
                "hozAlign": "center",
                "headerSort": false,
                "cellClick": "function(e, cell){ cell.getRow().toggleSelect();addSomethingHere()}",
                "width": "3%",
                "frozen": true
            },

Cheers
Andy

Hi

Interesting task. First I took a look into the docs:

As I read it ... (I can't test it right now):

  1. define the table as selectable
  2. define one callback to send an array of selected rows back
msg.ui_control.tabulator={
    "selectable":true, 
    "columns":[ define your columns here ],
    "rowSelectionChanged":"function(data, rows){this.send({ui_control:{'callback':'rowSelectionChanged','rowSelection':data}});}"
}

This is the callback in a readable form:

var rowSelectionChanged = function(data, rows){
    this.send({ui_control:{'callback':'rowSelectionChanged','rowSelection':data}}); 
}

hope this helps

1 Like

Thanks – that worked first time.

What I don’t fully understand is all the dozens of attempts I made either returned null arrays or just nothing.

When I started watching the console (chrome) a lot of my attempts would give the error:

“Uncaught RangeError: Maximum call stack size exceeded”

I didn’t have single quotes in the object names but that doesn’t seem to matter if I do or don’t as none of them have white space.

I tried replacing the rowSelectionChanged callback to rowSelected and that either gives no result or the RangeError.

I had tried using this.table.getSelectedRows() but that gave me the range error.

It would be quite useful to know why I get the range error but your suggestion gets me moving forward again.

Andy

Perhaps the stack problem is caused by not sending ui_control.callback property (as described in the readme of ui-table)

use this.send({}) to pass result to Node-RED. (to avoid a loopback add ui_control.callback="someText" )

Single quotes are easier to use because you do not have to escape them \” when using JSON formation.

Thanks.
I have the code working as needed but for my satisfaction can you explain what is going on that causes loopback and the range error? (And how to avoid it)

For example - this gives me the error when i try and return rows (not that I need to do this, i;m just trying to understand what cause the range error)

        "rowSelectionChanged": "function(data, rows){this.send({topic:'rowSelection',ui_control:{'callback':'rowSelection','rowList':rows}});}",

And this works fine - returning the data part as expected.

        "rowSelectionChanged": "function(data, rows){this.send({topic:'rowSelection',ui_control:{'callback':'rowSelection','rowList':data}});}",

Many thanks
Andy

Hi Andy,

Rows is not a JavaScript object per se it is a component which can not be transferred to a JSON formatted sting. You can/should access the data by the included methods.

This is why the send method fails.

You can pick the relevant data by using the methods in a new object and send this back to the runtime.

Here is an example for the cell component

So your error is not directly related to the loopback issue - your error is caused by trying to convert a component to a json string.

Thank you for your help - much appreciated and really useful.

The problem with my copy and tweak learning method is that it needs lots of attempts as there are a lot of combinations and most explanations omit key basic facts!

No problem ... try and error. We all learn most form mistakes and discoveries :wink:

1 Like

@Christian-Me
In one of your other posts (Now closed) you specified this example.

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

I cannot get that last line to work

id:cell.getRow().getCell('id').getValue()

if I change it to 'name' - like this:

id:cell.getRow().getCell('name').getValue()

it then works fine and returns the value of that name cell.

I have added id:1 etc at the beginning of the data rows definition.

{id:1,"name":"MEQ0451495","room":"Bathroom","SET_TEMPERATURE-value":22 ......................

Is the id field treated differently than a 'regular' field?

Hi,
Yes id field is a little bit special because it is the default index field. You can specify any field as the index by using the index property (use msg.ui_control.tabulator.index)
The index field is necessary if you like to update/delete or select a line or cell.
The latest version of ui-table should generate the id field/column automatically if not present in the payload.

But you are certainly free to define your own index field or data.

Row Index

A unique index value should be present for each row of data if you want to be able to programatically alter that data at a later point, this should be either numeric or a string. By default Tabulator will look for this value in the id field for the data. If you wish to use a different field as the index, set this using the index option parameter.


var table = new Tabulator("#example-table", { index:"age", //set the index field to the "age" field. });

Hi again @Christian-Me
So I can remove my definition of id:1 and allow it to be auto generated. That is fine and I will do that but how do I retrieve that id value as I am going to be updating a cell.
Trying to retrieve it like your code snippet doesn't work - for me at least!.
So I assume that some other method is required to get that id value but I cannot find it documented how to retrieve it inside of a cellEdited call back

id:cell.getRow().getCell('id').getValue()

Hi ...

did some tests and yes there is a little bit of a problem with the .getCell('id') if the id is hidden or was never visible. Could not find the cause, perhaps a bug of the tabulator version we use. But the .getIndex() from the row component should work.

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

and it is more versatile than specifying the index field name.

I updated the example flow too:

[{"id":"2e6a6379.742abc","type":"ui_table","z":"c4712650.59b5e8","group":"ff9fdb9a.7da098","name":"testTable","order":2,"width":"8","height":5,"columns":[{"field":"rowNumber","title":"Row Number 1","width":"","align":"left","formatter":"rownum","formatterParams":{"target":"_blank"}},{"field":"textValue","title":"Text","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"numberValue","title":"Number","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}}],"outputs":1,"cts":true,"x":808,"y":357,"wires":[["1c377ea0.5801e1","c0a33274.5dbeb"]]},{"id":"16664cef.5b26b3","type":"function","z":"c4712650.59b5e8","name":"table paramter","func":"msg.ui_control = {tabulator:{}};\n\n//workaround that buttos can`t deliver empty strings\nif (msg.payload.hasOwnProperty('payload')) {\n    msg.payload=msg.payload.payload;\n}\n\nmsg.ui_control.tabulator[msg.topic]=msg.payload;\ndelete msg.topic;\nmsg.payload=null;\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":646,"wires":[["2e6a6379.742abc","b8d75d24.6cbed"]]},{"id":"b8d75d24.6cbed","type":"debug","z":"c4712650.59b5e8","name":"table input","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":818,"y":646,"wires":[]},{"id":"1c377ea0.5801e1","type":"debug","z":"c4712650.59b5e8","name":"table output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":971,"y":357,"wires":[]},{"id":"b15c31a9.af37c","type":"ui_switch","z":"c4712650.59b5e8","name":"selectable","label":"selectable","tooltip":"","group":"ff9fdb9a.7da098","order":3,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"selectable","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":230,"y":442,"wires":[["16664cef.5b26b3"]]},{"id":"75207e8d.c54d4","type":"ui_switch","z":"c4712650.59b5e8","name":"movableColumns","label":"movableColumns","tooltip":"","group":"ff9fdb9a.7da098","order":5,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"movableColumns","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":260,"y":493,"wires":[["16664cef.5b26b3"]]},{"id":"c585e7a1.dfc648","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":6,"width":"4","height":"1","passthru":false,"label":"Format Numbers > 100","tooltip":"using a rowFormatter callback function","color":"","bgcolor":"","icon":"","payload":"function(row){         if(row.getData().numberValue>100){             row.getElement().style.backgroundColor = \"#A6A6DF\";         }     },","payloadType":"str","topic":"rowFormatter","x":280,"y":629,"wires":[["16664cef.5b26b3"]]},{"id":"f178c6fe.710ef8","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":1,"width":0,"height":0,"passthru":false,"label":"Fill demo data","tooltip":"","color":"","bgcolor":"","icon":"","payload":"[{\"textValue\":\"Line #1\",\"numberValue\":123.12},{\"textValue\":\"Line #2\",\"numberValue\":100},{\"textValue\":\"Line #3\",\"numberValue\":50}]","payloadType":"json","topic":"rowFormatter","x":250,"y":357,"wires":[["2e6a6379.742abc"]]},{"id":"2403f929.df4006","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":8,"width":"4","height":"1","passthru":false,"label":"inject Tooltips callback","tooltip":"cell.getColumn().getField() + \" - \" + cell.getValue();","color":"","bgcolor":"","icon":"","payload":"function(cell){return  cell.getColumn().getField() + \" - \" + cell.getValue(); },","payloadType":"str","topic":"tooltips","x":270,"y":731,"wires":[["16664cef.5b26b3"]]},{"id":"f6c68c45.58003","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":9,"width":"4","height":"1","passthru":false,"label":"clear Tooltips callback","tooltip":"empty string is not possible! so use a single space","color":"","bgcolor":"","icon":"","payload":"{\"payload\":\"\"}","payloadType":"json","topic":"tooltips","x":270,"y":765,"wires":[["16664cef.5b26b3"]]},{"id":"d2b29dda.60a5a","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":10,"width":"4","height":"1","passthru":false,"label":"reformat Number column","tooltip":"inject additional paramters to numberValue column","color":"","bgcolor":"","icon":"","payload":"[{\"field\":\"numberValue\",\"formatterParams\":{\"min\":0,\"max\":200,\"legend\":\"function (value) {     if (value<100) return \\\"<span style='color:#FF0000;'>\\\"+value+\\\"</span>\\\";     else return \\\"<span style='color:#000000;'>\\\"+value+\\\"</span>\\\";  }\",\"legendAlign\":\"center\"},\"formatter\":\"progress\",\"topCalc\":\"function(values, data, calcParams){     var total = 0;     var calc = 0;     var count = 0;     data.forEach(value => {         total+=Number(value.numberValue);         count++;     });     if (count>0) calc=(total/count).toFixed(2);     return `${calc} (avg)`; }\",\"headerTooltip\":\"avarage\"}]","payloadType":"json","topic":"columns","x":280,"y":833,"wires":[["16664cef.5b26b3"]]},{"id":"7b6490b3.cd9c7","type":"function","z":"c4712650.59b5e8","name":"callback function(s)","func":"// how to use the editor to write callback functions\n// DO NOT wire this into your flow!\n// copy / paste \"function( ... }\" into the correct calback parameter\n// use the \"debugger\" statement to debug your callback inside your browser\n\nvar topCalc = function(values, data, calcParams){\n    var total = 0;\n    var calc = 0;\n    var count = 0;\n    data.forEach(value => {\n        total+=Number(value.numberValue);\n        count++;\n    });\n    if (count>0) calc=(total/count).toFixed(2);\n    return `${calc} (avg)`;\n}\n\nvar legend = function (value) {\n    if (value<100) return \"<span style='color:#FF0000;'>\"+value+\"</span>\";\n    else return \"<span style='color:#000000;'>\"+value+\"</span>\"; \n}\n\nvar cellEdited = function(cell){\n    this.send({\n        ui_control:{callback:'cellEdited'},         \n        payload:cell.getValue(),\n        oldValue:cell.getOldValue(),\n        field:cell.getColumn().getField(),\n        id:cell.getRow().getIndex()\n    }); \n}","outputs":1,"noerr":0,"x":600,"y":765,"wires":[[]]},{"id":"91506d4b.4956a","type":"comment","z":"c4712650.59b5e8","name":"Intentionally not wired into the flow!","info":"use the editor to write callback functions\n\nDO NOT wire this into your flow!\n\ncopy / paste `function( ... }` into the correct calback parameter\nuse the `debugger` statement to debug your callback inside your browser\n","x":650,"y":731,"wires":[]},{"id":"732afcea.f728f4","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":11,"width":"4","height":"1","passthru":false,"label":"reset Number column","tooltip":"inject additional paramters to numberValue column","color":"","bgcolor":"","icon":"","payload":"[{\"field\":\"numberValue\",\"formatter\":\"plaintext\",\"topCalc\":\"\",\"headerTooltip\":\"\"}]","payloadType":"json","topic":"columns","x":270,"y":867,"wires":[["16664cef.5b26b3"]]},{"id":"89cca7ea.7fc998","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":12,"width":"4","height":"1","passthru":false,"label":"add/show id column","tooltip":"add a new column","color":"","bgcolor":"","icon":"","payload":"[{\"field\":\"id\",\"title\":\"id\",\"visible\":true,\"formatter\":\"plaintext\"}]","payloadType":"json","topic":"columns","x":270,"y":935,"wires":[["16664cef.5b26b3"]]},{"id":"32a3c4ad.1b85fc","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":13,"width":"4","height":"1","passthru":false,"label":"hide id column","tooltip":"hide id column (it is not possible to delete a existing column definition)","color":"","bgcolor":"","icon":"","payload":"[{\"field\":\"id\",\"title\":\"id\",\"visible\":false,\"formatter\":\"plaintext\"}]","payloadType":"json","topic":"columns","x":250,"y":969,"wires":[["16664cef.5b26b3"]]},{"id":"25247f4b.cc7ec","type":"inject","z":"c4712650.59b5e8","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":84,"y":493,"wires":[["75207e8d.c54d4","bd3fd382.a2aa9"]]},{"id":"18ed8d27.bf00a3","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":7,"width":"4","height":"1","passthru":false,"label":"reset Numbers > 100","tooltip":"using a rowFormatter callback function","color":"","bgcolor":"","icon":"","payload":"{\"payload\":\"\"}","payloadType":"json","topic":"rowFormatter","x":270,"y":663,"wires":[["16664cef.5b26b3"]]},{"id":"bd3fd382.a2aa9","type":"ui_switch","z":"c4712650.59b5e8","name":"headerVisible","label":"headerVisible","tooltip":"","group":"ff9fdb9a.7da098","order":4,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"headerVisible","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":250,"y":544,"wires":[["16664cef.5b26b3"]]},{"id":"ce8d3904.be1b08","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":15,"width":"4","height":"1","passthru":false,"label":"inject cellEdited callback","tooltip":"","color":"","bgcolor":"","icon":"","payload":"function(cell){     this.send({         ui_control:{callback:'cellEdited'},                  payload:cell.getValue(),         oldValue:cell.getOldValue(),         field:cell.getColumn().getField(),         id:cell.getRow().getIndex()     });  }","payloadType":"str","topic":"cellEdited","x":280,"y":1071,"wires":[["16664cef.5b26b3"]]},{"id":"f7b8bb20.5217f8","type":"ui_button","z":"c4712650.59b5e8","name":"","group":"ff9fdb9a.7da098","order":14,"width":"4","height":"1","passthru":false,"label":"enable cell edit (Text)","tooltip":"set \"editor\" to \"input\"","color":"","bgcolor":"","icon":"","payload":"[{\"field\":\"textValue\",\"title\":\"Text\",\"editor\":\"input\",\"formatter\":\"plaintext\"}]","payloadType":"json","topic":"columns","x":270,"y":1037,"wires":[["16664cef.5b26b3"]]},{"id":"c0a33274.5dbeb","type":"ui_text","z":"c4712650.59b5e8","group":"ff9fdb9a.7da098","order":16,"width":0,"height":0,"name":"","label":"table Output","format":"{{msg.payload}} id:{{msg.id}}","layout":"row-spread","x":971,"y":391,"wires":[]},{"id":"ff9fdb9a.7da098","type":"ui_group","z":"","name":"TEST","tab":"7dcc246f.ee661c","order":1,"disp":false,"width":"8","collapse":false},{"id":"7dcc246f.ee661c","type":"ui_tab","z":"","name":"TEST","icon":"dashboard","order":12,"disabled":false,"hidden":false}]
2 Likes

aha - thanks . I assume I was doing something wrong in not being able to get that id field.
I have just tried id:cell.getRow().getIndex() - and it works if I now include id back into the input payload. !!

Do you know how to turn off the 'noise' that is being pushed out to msg.payload.
I only have a cellEdited callback as I want to be able to update single cells in a column. Not bothered about anything else. so I have no other callbacks specified.

Yet, I am finding that If I simply click on any cell in any row, I get a message pushed out to debug.
I do not have cellClick callback present. The only output that I want to get from the table is the cellEdited - and that piece is working fine.

Hi Dave,

the chellClick callback is part of the "vanilla" function of ui-table. See info panel.

You can disable it by sending a empty string.

{
  "payload":null,
  "ui_control":
    {"tabulator":{"cellClick":""}}
}

or an empty function.
This is due to the mix iterative configuration based on the core functionalities.

A) core configuration done inside the editor ui
B) merge any additional config via ui_control

Hope this helps.

oh jeez. Where is that documented?
I have spent some hours wading through the Tabulator doc and what there is of UI Table but never came across this.

You say look in the info panel - I am but not seeing it ???.

Hi Dave,
I meant vanilla Node-RED dashboard widget ui-table not tabulator directly.

To achieve this function the cellClick callback is used.
I know the latest ui-control feature upgrade is not documented in detail. Most is described by the example flow. Sorry for that it is only lack of time that docs are not always up to date. (BTW who RTFM anyway? - I do but ...)

In addition for anybody interested:

How customizing ui-table with ui_controll messages work is:

  • tabulator is configured by the settings in the editor UI
  • messages with a msg.ui_control object will add or modify features of tabulator. It do not replace the configuration done in the UI
  • subsequent ui_control messages are merged into the tabulator object! So they do not replace the existing config
  • you can modify existing column config of individual columns. The column is matched by the field property.
  • if you do not pass a payload with in your msg to ui-table containing ui_control the payload has to be null (this is necessary to trick the dashboard a little bit to pass ui_control objects unaltered to ui-table to perform the merge and column matching)

This is the default tabulator configuration:

{
  layout: 'fitColumns',
  autoColumns: columndata.length == 0,
  movableColumns: true,
  cellClick: 'function(e, cell) { $scope.send({topic:cell.getField(), payload:cell.getData(), row:(cell.getRow()).getPosition()}); };'
  columns: columndata,
  data: tabledata,
}
1 Like

Hello Christian,

I would like to ask, how do you do this 2 Colum Group ( Measurment & Settings) with other Columns? Because I try but I get only 1 ( the last in the column list).

here is my code of ui_control:

{
"formatterParams": {
"target": "_blank"
},
"title": "Measurements",
"columns": [
{
"title": "Value",
"field": "sensor_value",
"formatter": "plaintext",
"align": "center"
},
{
"title": "Units",
"field": "units",
"formatter": "plaintext",
"align": "center",
"width": 50
}
]
},
{
"formatterParams": {
"target": "_blank"
},
"title": "Location",
"columns": [
{
"title": "Latitude",
"field": "lat",
"formatter": "plaintext"
},
{
"title": "Longitude",
"field": "long",
"formatter": "plaintext",
"align": "center"
}
]
}

So Is that I can get it, the three structure is not perfect decoded ??

{
   "formatterParams": {
   "target": "_blank"
     },
        "title": "Measurements",
         "columns": [
               {
                  "title": "Value",
                  "field": "sensor_value",
                  "formatter": "plaintext",
                  "align": "center"
               },
               {
                  "title": "Units",
                  "field": "units",
                  "formatter": "plaintext",
                  "align": "center",
                  "width": 50
             }
           ]
        },
        {
           "formatterParams": {
                 "target": "_blank"
            },
            "title": "Location",
            "columns": [
                  {
                      "title": "Latitude",
                       "field": "lat",
                       "formatter": "plaintext"
                  },
                  {
                       "title": "Longitude",
                       "field": "long",
                       "formatter": "plaintext",
                       "align": "center"
                  }
            ]
        }

You only need for URL links to open the link on a new Tab. Can be deleted

Something is missing at the beginning too. Please refer to one of the examples of ui-table for a complete JSON object.
Perhaps paste your JSON into a JSON editor (Any node red node which imput field you can switch to json like the change node / online validators / vs code or similar) and validate it.