Using copy paste table data via dashboard

Hmm, not entirely convinced. Depends a lot I think on the clipboard formatting and whether the browser allows entry of enriched text.

Good news though is that it made me look at the library being used by the Dashboard table node. I realised that it no longer depends on jQuery so I'm looking to see how useful it is in uibuilder. :grinning:

Right but you can use build in or your own parsers.

As far as I know tabulator is pure JavaScript.

Perhaps my current project interests you

I’m currently designing a flow for displaying health information of many external devices originally based on the homie convention but expandable through individual nodes:
1.) data acquisition: translation of the data into homie and individual user defined fields (not everything fits into the homie convention but it is a good common ground)
2.) plugins: little nodes doing all kind of stuff with this data. For example a watchdog, a reset counter, max. min. or avg. uptime, add icons from the values like battery or signal and other things. To make these reusable it is essential to have a common dataset defined.
3.) table handling: universal flow to handle table formatting, data storage, column width and order, cell editing and interactive stuff like context menus.

Would be great if 1 and 2 work on the dashboard and uibuilder together that there will be a variation of plugins for different devices / firmware or useful filters usable in all scenarios.
My idea is that through simple nodes = plugins all kind of devices could be included and configured as necessary. I already have homie, mqtt in any shape or form, Homematic and http (ESPEasy json responses). Tasmota, ESP Home and others are certainly possible.

Let me know if you are interested.

4 Likes

Looks interesting. Got to pop out with the family now so don't have time to respond properly.

I too have a dashboard but it is more focused on heating right now and as part of my re-platforming from Pi to old laptop, I'm going to re-engineer a lot of my data so the ideas in your image are certainly interesting.

I don't use Dashboard though but rather uibuilder and I've currently side-tracked myself as I'm building a reusable editable table component. I've been using the "native" bootstrap-vue table component as a base but I've also been playing with Tabulator which is easy enough to get to work with Vue. Slightly concerned about size though, especially as the date transformations require MomentJS which isn't small.

You've sparked a few thoughts though.

I'm interested in how you translate from RSSI to the WiFi icon. Also interested in the other chart-like columns, what is their meaning?

Yes, it is now, previously it was dependent on jQuery and jQuery-UI.

Spend time with the family, more important than this stuff. I have to give a little love to my garden ... it`s spring is coming

You mean this columns? Tabulator has a tool-tip function build in :wink: But I could not do a screenshot (geenshot is nice, but sometimes ...)
Power supply (V), battery state (%), Free Heap (in bytes but converted in tabulator in KB) and CPU Load. CPU Temp is hidden (ESPs do not have a cpu temp sensor). Most of them using the progress bar format with a custom label formater to show the units and convert bytes in kB.
image
The base dataset is defined by the homie convention 3.0.1 and 4.0.0 with $state and $fw extensions. Other data comes form a http get on a ESPEasy device: (here you see the responsive layout feature of tabulator (there are sill a few issues in the tabulator version in ui-table with dynamic updates to investigate but works for now quite fine.


Adding Icons is done by one (I call them) plugin

[{"id":"db94aa7b.554e88","type":"function","z":"9d52d40a.a63668","name":"add icons","func":"var icons= {\n    \"$state\":[\n        {\"value\":\"init\",\"icon\":\"fa fa-cog fa-spin\"},\n        {\"value\":\"ready\",\"icon\":\"fa fa-spinner fa-spin\"},\n        {\"value\":\"disconnected\",\"icon\":\"fa fa-times\"},\n        {\"value\":\"sleeping\",\"icon\":\"fa fa-moon-o\"},\n        {\"value\":\"lost\",\"icon\":\"fa fa-question-circle\"},\n        {\"value\":\"lostBroker\",\"icon\":\"fa fa-exclamation-triangle\"},\n        {\"value\":\"alert\",\"icon\":\"fa fa-exclamation-triangle\"}\n    ],\n    \"signal\":[\n        {\"value\":10,\"icon\":\"perm_scan_wifi\",\"style\":\"color:#cc0000\"},\n        {\"value\":20,\"icon\":\"wifi\",\"style\":\"color:#cc3300\"},\n        {\"value\":30,\"icon\":\"wifi\",\"style\":\"color:#cc6600\"},\n        {\"value\":40,\"icon\":\"wifi\",\"style\":\"color:#cc9900\"},\n        {\"value\":50,\"icon\":\"wifi\",\"style\":\"color:#cccc00\"},\n        {\"value\":60,\"icon\":\"wifi\",\"style\":\"color:#99cc00\"},\n        {\"value\":70,\"icon\":\"wifi\",\"style\":\"color:#66cc00\"},\n        {\"value\":80,\"icon\":\"wifi\",\"style\":\"color:#33cc00\"},\n        {\"value\":100,\"icon\":\"signal_wifi_4_bar\",\"style\":\"color:#00cc00\"},\n    ],\n    \"battery\":[\n        {\"value\":10,\"icon\":\"battery-alert\",\"style\":\"color:#cc0000\"},\n        {\"value\":20,\"icon\":\"battery_20\"},\n        {\"value\":30,\"icon\":\"battery_30\"},\n        {\"value\":50,\"icon\":\"battery_50\"},\n        {\"value\":60,\"icon\":\"battery_60\"},\n        {\"value\":70,\"icon\":\"battery_70\"},\n        {\"value\":80,\"icon\":\"battery_80\"},\n        {\"value\":90,\"icon\":\"battery_90\"},\n        {\"value\":100,\"icon\":\"battery_full\",\"style\":\"color:#00cc00\"},\n    ]\n};\n\nvar status = {fill:\"yellow\",shape:\"dot\",text: \"\"};\nif (msg.hasOwnProperty(\"state\")) {\n    status.text=\"device=\";\n    status.text+=(msg.state.$name) ? msg.state.$name : msg.topic;\n    var formatHtml=function(icon) {\n        var html=\"\"\n        if (icon.icon.startsWith('fa')) {\n            html = \"<i class=\\\"\"+icon.icon+'\\\"></i>'; \n        } else {\n            html+='<i class=\"material-icons\"';\n            if (icon.hasOwnProperty(\"style\")) html+=' style=\"'+icon.style+'\"';\n            html+='>'+icon.icon+'</i>';                               \n        }\n        return html;\n    }\n    nextProperty: \n    for (var state in msg.state) {\n        if (icons.hasOwnProperty(state)) {\n            for (var icon of icons[state]) {\n                switch (typeof msg.state[state]) {\n                    case \"string\":\n                        if (msg.state[state]===icon.value) {\n                            msg.state[state+\"Icon\"]=formatHtml(icon);\n                            status.fill=\"green\";\n                            status.text+=state+\" \";\n                            continue nextProperty;\n                        }\n                        break;    \n                    case \"number\":\n                        if (msg.state[state]<=icon.value) {\n                            msg.state[state+\"Icon\"]=formatHtml(icon);\n                            status.fill=\"green\";\n                            status.text+=state+\" \";\n                            continue nextProperty;\n                        }\n                        break;\n                }\n            }\n        }\n    }    \n}\nnode.status(status);\nreturn msg;","outputs":1,"noerr":0,"x":332,"y":476,"wires":[["248e0178.34da0e"]],"icon":"font-awesome/fa-wifi","info":"# Add and format icons\n- This node is configured by a json object. \n- All incomming data inside `msg.state`is inspected to the parent keys of this object. \n- The value contains an array of objects defining differend icons and styles to be added.\n- a html formatted string is added to `msg.state.[key+\"Icon\"]`\n- the current version supports font awesome icons supported by the dshboard and material icons including the style and size [ToDo] property\n- Sting values will be compared for a case sensitive match\n- values will be compared `<=`. So arrange the objects in a increasing order.\n\n## configuration object\n- defining the keys to be triggered\n```json\n{\n    \"$state\":[],\n    \"signal\":[],\n    \"battery\":[]\n}\n```\n- to trigger on a the value \"init\" to get a font awsome icon plce ´fa´ at the beginning of the `icon` value\n```json\n{\n    \"value\":\"init\",\n    \"icon\":\"fa fa-cog fa-spin\"\n}\n```\nfor material icons you can use\n```json\n{\n    \"value\":10,\n    \"icon\":\"perm_scan_wifi\",\n    \"style\":\"color:#cc0000\"\n}\n```\n"}]

As homie defines $stats\signal in % I have done this in C inside the homie protocol plugin of ESPEasy. But other devices can use this translator I also use for old devices running ESPEasy without homie using the system info plugin of ESPEasy:

[{"id":"a89f27b1.0d13d8","type":"function","z":"9d52d40a.a63668","name":"translate homie","func":"\n// IMPORTTANT! \n// Make shure that the topic matches the topit of homie state node that the extra data is merged correctly!\nmsg.topic=\"mqtt://192.168.2.14:1883/homie/\"+msg.deviceId;\n// if you do not use the homie convention choose a unique indetifier as msg.topic:\n// msg.topic=msg.payload.System[\"Unit Name\"];\n\nmsg.state={\"$name\":msg.deviceId,\"$state\":\"ready\"};\nswitch (msg.propertyId.toLowerCase()) {\n    // messages that need conversion\n    case ('rssi'):\n        msg.state.signal=Math.min(Math.max(2 * (msg.value + 100), 0), 100)\n        break;\n    // all messages that can be translated 1:1    \n    case ('freeheap'):\n    case ('uptime'):\n        msg.state[msg.propertyId]=msg.value;\n        break;\n    default: // drop message\n        return;\n}\nreturn msg;","outputs":1,"noerr":0,"x":1083,"y":187,"wires":[["9e278daa.10534","f6956de0.9b59d"]],"icon":"node-red/swap.svg"}]

This is using the simple linear approach to convert db into %. Not precise but ok.
msg.state.signal=Math.min(Math.max(2 * (msg.value + 100), 0), 100)

And finally this is how the hole flow currently looks like (forget all the debug nodes then it is not so complicated).

  1. Data acquisition, translation and plugins
  2. Dashboard (ui-table and context menues)

    The ui-table handler subflow is doing all the heavy lifting like holding the table layout, row data, individual cell edits, column width, hidden flag and order, handling commands coming from the context menus and so on. I tried to make it "reusable" for other purposes when I use ui-table like showing actual data:
    image
    What was intended as a quick answer got much to long, sorry.
3 Likes

Nice.

No sign of spring here! Just cold, wet and windy. Driving wife and daughter round different fabric shops while they looked for fabric for daughter's prom dress. Followed by coffee and creme scone at a local stately home. :innocent:

Thanks for sharing. Interesting and certainly some ideas I'm going to investigate in my own new dashboard/control system.

Actually this helps a lot but i don´t undestand how to make my flow with html i will put my code @Christian-Me [{"id":"2965a5e7.2809fa","type":"tab","label":"Flow 6","disabled":false,"info":""},{"id":"95273205.a823","type":"ui_table","z":"2965a5e7.2809fa","group":"615b9ae0.a9d4c4","name":"","order":6,"width":0,"height":0,"columns":[],"outputs":1,"cts":true,"x":870,"y":240,"wires":[[]]},{"id":"d12e67b8.c63008","type":"template","z":"2965a5e7.2809fa","name":"","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"<div id=\"example-table\"></div>\n//define data\nvar tabledata = [\n {id:1, name:\"Oli Bob\", location:\"United Kingdom\", gender:\"male\", rating:1, col:\"red\", dob:\"14/04/1984\"},\n {id:2, name:\"Mary May\", location:\"Germany\", gender:\"female\", rating:2, col:\"blue\", dob:\"14/05/1982\"},\n {id:3, name:\"Christine Lobowski\", location:\"France\", gender:\"female\", rating:0, col:\"green\", dob:\"22/05/1982\"},\n {id:4, name:\"Brendon Philips\", location:\"USA\", gender:\"male\", rating:1, col:\"orange\", dob:\"01/08/1980\"},\n {id:5, name:\"Margret Marmajuke\", location:\"Canada\", gender:\"female\", rating:5, col:\"yellow\", dob:\"31/01/1999\"},\n {id:6, name:\"Frank Harbours\", location:\"Russia\", gender:\"male\", rating:4, col:\"red\", dob:\"12/05/1966\"},\n {id:7, name:\"Jamie Newhart\", location:\"India\", gender:\"male\", rating:3, col:\"green\", dob:\"14/05/1985\"},\n {id:8, name:\"Gemma Jane\", location:\"China\", gender:\"female\", rating:0, col:\"red\", dob:\"22/05/1982\"},\n {id:9, name:\"Emily Sykes\", location:\"South Korea\", gender:\"female\", rating:1, col:\"maroon\", dob:\"11/11/1970\"},\n {id:10, name:\"James Newman\", location:\"Japan\", gender:\"male\", rating:5, col:\"red\", dob:\"22/03/1998\"},\n];\n\n//define table\nvar table = new Tabulator(\"#example-table\", {\n data:tabledata,\n autoColumns:true,\n});","output":"str","x":540,"y":280,"wires":[["818f2a54.e9bb08"]]},{"id":"818f2a54.e9bb08","type":"json","z":"2965a5e7.2809fa","name":"","property":"payload","action":"","pretty":false,"x":670,"y":260,"wires":[["95273205.a823"]]},{"id":"5cd72d5d.f7d494","type":"template","z":"2965a5e7.2809fa","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div id=\"example-table\"></div>","output":"str","x":440,"y":200,"wires":[["d12e67b8.c63008"]]},{"id":"ab9309a5.aaca18","type":"inject","z":"2965a5e7.2809fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":240,"y":180,"wires":[["5cd72d5d.f7d494"]]},{"id":"615b9ae0.a9d4c4","type":"ui_group","z":"","name":"Lamp","tab":"8f4c42b3.c0e6","order":2,"disp":true,"width":"6","collapse":false},{"id":"8f4c42b3.c0e6","type":"ui_tab","z":"","name":"Room","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

hi i think this is what i was looking for but actually i'm kind of new in this and i don't know much how to use the funcions and the html @TotallyInformation

that is amazing, i don't get much about what you do but tha's impressive @Christian-Me

Nice! :sunglasses:

That overview has been on my To-Do list for some time now as well and has just moved up a few items. :grin:

Yes, sorry, we've gotten slightly off track as we often do here!

I think that the bottom line is that it isn't all that easy to paste data from Excel to a web page. Indeed, creating a data-driven table on a web page is quite hard, even with the excellent help from Node-RED and the Dashboard table addon.

There are probably other ways you could achieve something though. Personally, I would probably use a CSV file rather than an Excel workbook format (.xls, etc) and save it to somewhere that Node-RED could access it. Then you can easily consume it in Node-RED.

Hi,

I Imported your flow ... a litte bit confusing. You do not need any Template HTML if you use ui-table. Perhaps take a look in the examples of UI-Table first (Menu/Import/Example)

instead of

var table = new Tabulator("#example-table", {
 data:tabledata,
 autoColumns:true,
});

use msg.ui_control.tabulator message to configure tabulator as needed.
See Example 3 ui_control table

Annoyingly, ui-table isn't working for me at all at the moment. New issue raised:

Thanks to @TotallyInformation and @Christian-Me you guys are really helpfull and nice i will wait to ui-table node will works actually since yesterday i was like nothing appears, i will continue working on my dashboard.

@Christian-Me just help me with this i think i got pretty much of the example, but i have been trying this weeken and i really don't know how can i use my function to paste the information i will put you the flow and also the place where i'm getting information. [{"id":"2965a5e7.2809fa","type":"tab","label":"Flow 6","disabled":false,"info":""},{"id":"10b9e62c.99b31a","type":"inject","z":"2965a5e7.2809fa","name":"","topic":"","payload":"[{\"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},{\"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},{\"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},{\"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},{\"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},{\"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},{\"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},{\"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},{\"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},{\"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}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":330,"y":260,"wires":[["dd80fbba.a3dfd8"]]},{"id":"dd80fbba.a3dfd8","type":"change","z":"2965a5e7.2809fa","name":"ui_control","rules":[{"t":"set","p":"ui_control.tabulator","pt":"msg","to":"{\"tabulator\":{\"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.field}); }); this.send({topic:this.config.topic,ui_control:{callback:'columnMoved',columns:newColumns}}); }\",\"groupHeader\":\"function (value, count, data, group) {return value + \\\"<span style='color:#d00; margin-left:10px;'>(\\\" + count + \\\" Termostat\\\"+((count>1) ? \\\"e\\\" : \\\"\\\") + \\\")</span>\\\";}\",\"columns\":[{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"ROom\",\"field\":\"room\",\"width\":100,\"frozen\":true},{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Device\",\"field\":\"name\",\"width\":100,\"align\":\"center\"},{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Type\",\"field\":\"deviceType\",\"width\":100,\"align\":\"center\"},{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Measurements\",\"columns\":[{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"target\",\"field\":\"SET_TEMPERATURE-value\",\"formatter\":\"function(cell, formatterParams, onRendered){return cell.getValue()+'°C';}\",\"topCalc\":\"avg\",\"width\":100},{\"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},{\"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},{\"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}]},{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Settings\",\"columns\":[{\"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},{\"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\"},{\"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\"},{\"formatterParams\":{\"target\":\"_blank\"},\"title\":\"Auto\",\"field\":\"AUTO_MODE-value\",\"formatter\":\"tick\",\"width\":100,\"align\":\"center\"}]}],\"layout\":\"fitColumns\",\"movableColumns\":true,\"groupBy\":\"\"},\"customHeight\":12}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":140,"wires":[["566e5c92.161a74"]]},{"id":"c358ec1c.ffd55","type":"debug","z":"2965a5e7.2809fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":789,"y":260,"wires":[]},{"id":"566e5c92.161a74","type":"ui_table","z":"2965a5e7.2809fa","group":"15157f81.1db74","name":"Thermostats","order":1,"width":"20","height":"7","columns":[],"outputs":1,"cts":true,"x":639,"y":260,"wires":[["c358ec1c.ffd55"]]},{"id":"2219afcd.494d1","type":"function","z":"2965a5e7.2809fa","name":"","func":"var table = new Tabulator(\"#example-table\", {\n height:\"311px\",\n data:tabledata,\n clipboard:true,\n clipboardPasteAction:\"replace\",\n columns:[\n {title:\"Name\", field:\"name\", width:200},\n {title:\"Progress\", field:\"progress\", width:100, sorter:\"number\"},\n {title:\"Gender\", field:\"gender\"},\n {title:\"Rating\", field:\"rating\", width:80},\n {title:\"Favourite Color\", field:\"col\"},\n {title:\"Date Of Birth\", field:\"dob\", align:\"center\", sorter:\"date\"},\n {title:\"Driver\", field:\"car\", align:\"center\", formatter:\"tickCross\"},\n ],\n });","outputs":1,"noerr":0,"x":470,"y":260,"wires":[[]]},{"id":"15157f81.1db74","type":"ui_group","z":"","name":"ui_control","tab":"1079ad9e.6b2382","order":1,"disp":true,"width":"22","collapse":false},{"id":"1079ad9e.6b2382","type":"ui_tab","z":"","name":"Home","icon":"track_changes","order":1,"disabled":false,"hidden":false}]. http://tabulator.info/docs/4.4/clipboard

Hi Eduardo,

I gave it a try on my train ride to the office and copy past is totally doable! Interesting way to enter data into Node-RED via the dashboard.
I will prepare a demo on my way home ...

Chris

3 Likes

Thanks @Christian-Me i really aprecciate that

And here you are:


You need only one callback function I included in a separate function note for better readability (do not wire this into your flow).
The change node does the preparation of the table with a msg.ui_control object. Because you need minimum one cell to paste into (you can`t paste into an empty table) I put in some dummy data
It is not necessary to configure the the table node only tick "send data on click" to activate the output.

And in Node-RED:
image

Here is the fow:

[{"id":"4512106a.2f145","type":"change","z":"4ddb981f.f34df8","name":"ui_control & placeholder","rules":[{"t":"set","p":"ui_control","pt":"msg","to":"{\"customHeight\":20,\"tabulator\":{\"layout\":\"fitColumns\",\"clipboard\":true,\"clipboardCopySelector\":\"table\",\"clipboardCopyStyled\":false,\"clipboardPasteAction\":\"function(rowData){     this.send({ui_control:{callback:'clipboardPasteAction'},payload:rowData});     return this.table.replaceData(rowData); }\",\"columns\":[{\"field\":\"col1\",\"title\":\"Column #1\"},{\"field\":\"col2\",\"title\":\"Column #2\"},{\"field\":\"col3\",\"title\":\"Column #3\"}]}}","tot":"json"},{"t":"set","p":"payload","pt":"msg","to":"[{\"col1\":\"click here & CTRL+v\",\"col2\":\"or click here & CTRL+v\",\"col3\":\"or click here & CTRL+v\"},{\"col1\":\" or on any other cell\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"\",\"col2\":\"\",\"col3\":\"\"},{\"col1\":\"there is a issue with small or empty tables\",\"col2\":\"will be dixed soon\",\"col3\":\"\"},{\"col1\":\"Untill then we have\",\"col2\":\"to fill in some\",\"col3\":\"dummy lines\"}]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":399,"y":255,"wires":[["71d16773.2b0a38"]]},{"id":"8d8d7ea4.2fe3f","type":"debug","z":"4ddb981f.f34df8","name":"clipboardPasteAction","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1018,"y":238,"wires":[]},{"id":"71d16773.2b0a38","type":"ui_table","z":"4ddb981f.f34df8","group":"113a4af6.0aab85","name":"Copy & Paste","order":1,"width":"17","height":"7","columns":[],"outputs":1,"cts":true,"x":641,"y":255,"wires":[["d9789454.020d58"]]},{"id":"a0dee6b9.6423c8","type":"function","z":"4ddb981f.f34df8","name":"clipboardPasteAction Callback","func":"// callback: clipboardPasteAction\n// DO NOT USE IN A FLOW!\n\nclipboardPasteAction = \n// copy from here\nfunction(rowData){\n    this.send({ui_control:{callback:'clipboardPasteAction'},payload:rowData});\n    return this.table.replaceData(rowData);\n}\n// until here\n// use the Visual Editor!\n// paste into ui_table JSON \"clipboardPasteAction\":\"function(....\"\n\n// technical details\n//\n// the example function ends with\n//\n// return this.table.updateData(rows);\n// \n// but that's not updating the data (because it does try to update data through\n// the index field (default \"id\")\n// replaceData simply erase all existing rowData and fills in the pasted data\n// that's what we want","outputs":1,"noerr":0,"x":419,"y":204,"wires":[[]]},{"id":"38c0e825.798328","type":"ui_ui_control","z":"4ddb981f.f34df8","name":"on tab change","events":"change","x":182,"y":255,"wires":[["4512106a.2f145"]]},{"id":"d9789454.020d58","type":"switch","z":"4ddb981f.f34df8","name":"","property":"ui_control.callback","propertyType":"msg","rules":[{"t":"eq","v":"clipboardPasteAction","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":815,"y":255,"wires":[["8d8d7ea4.2fe3f"],["7b094d08.4d1224"]]},{"id":"7b094d08.4d1224","type":"debug","z":"4ddb981f.f34df8","name":"something else","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":998,"y":272,"wires":[]},{"id":"113a4af6.0aab85","type":"ui_group","z":"","name":"ui-table Copy & Paste","tab":"a1c14418.954cd8","disp":true,"width":"17","collapse":false},{"id":"a1c14418.954cd8","type":"ui_tab","z":"","name":"ui-table test","icon":"dashboard","disabled":false,"hidden":false}]

CTRL-c is working too. It is set to "table" (including currently invisble cells) and no styling.

Detailed documentation of this tabulator feature can be found here.

Be aware that colum field names must match cells of the first row of the pasted data and the clipboard is tab \t and new line \n formatted, as windows clipboard does. http://tabulator.info/docs/4.5/clipboard#paste-parser

There could be still a little issue in the vertical size of the table. So this is why the table is initialized with blank or dummy rows. Will be solved shortly

2 Likes

Thanks, that was really helpfull i will share my flow when i finish. And sorry for all the request

Hi Eduardo,
hope it work on your side. No problem always interested in unusual problems to solve.
Chris

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.