Add rows to a table dynamically with msg.payload

Hi,

I am trying to create a table where every time a new msg.payload object comes in it adds the information to the table on the dashboard. (Up to 5).

The problem is that when a new payload comes it overwrites the previous and does not add a new row. So I have 1 row table that keeps updating the same row...

Here a simpler example of that part of my code:

[{"id":"288e4e3c.342d32","type":"inject","z":"3028bfa2.159bb8","name":"Go","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"","x":269,"y":270,"wires":[["6802f4ef.57e17c"]]},{"id":"6802f4ef.57e17c","type":"function","z":"3028bfa2.159bb8","name":"MyJson","func":"msg.payload =\n{\n    prediction: {\n    \n        \"a\": Math.floor(Math.random() * (20)),\n        \"b\": Math.floor(Math.random() * (20)),\n        \"c\": Math.floor(Math.random() * (20)),\n        \"d\": Math.floor(Math.random() * (20)),\n        \"e\": Math.floor(Math.random() * (20)),\n    }\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":411.5,"y":270,"wires":[["67785428.40efac","3e574e09.fef4d2"]]},{"id":"67785428.40efac","type":"debug","z":"3028bfa2.159bb8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":547,"y":339,"wires":[]},{"id":"3e574e09.fef4d2","type":"ui_template","z":"3028bfa2.159bb8","group":"21ec3303.4052a4","name":"","order":0,"width":"12","height":"4","format":"<table style=\"width:100%\">\n  <tr>\n    <th>column1</th> \n    <th>column2</th>\n    <th>column3</th> \n    <th>column4</th>\n    <th>column5</th>\n  </tr>\n  <tr ng-repeat=\"x in msg.payload | limitTo:5\">\n    <td>{{prediction}}</td>\n    <td>{{msg.payload.prediction.a}}</td>\n    <td>{{msg.payload.prediction.b}}</td> \n    <td>{{msg.payload.prediction.c}}</td>\n    <td>{{msg.payload.prediction.d}}</td>\n    <td>{{msg.payload.prediction.e}}</td>\n  </tr>\n</table>","storeOutMessages":false,"fwdInMessages":true,"templateScope":"local","x":585.5,"y":270,"wires":[[]]},{"id":"21ec3303.4052a4","type":"ui_group","z":"","name":"Column 2","tab":"c728098a.279a3","order":2,"disp":false,"width":"12","collapse":false},{"id":"c728098a.279a3","type":"ui_tab","z":"","name":"Feeder test","icon":"dashboard"}]

Thanks! :blush:

I’m curious to how this comes out as well.
It’s something that I have wanted to do but just have not had the time.

Thinking of it now, I would start by adding a ui template of my table with headers.

Within the same template you will need to add a script section that watches for a new msg coming into the template node.
The particular would be scope.$watch function.
You can search this forum and Google for more info.
Within that function, you can use some jQuery to add a new row.
A search of Google found this little nugget.

Also, this post in stack overflow (tks Steve) may be useful for your use case in case you are not comfortable on using scope.$watch()

You will need to just resend the array of data to the template, so need to develop some code, possibly using context) to store data in an array and a logic to send only the amount of lines you you to the template (you mentioned up to five , right ?)..

1 Like

maybe this can help?
https://flows.nodered.org/flow/8a0959d742e9af9517312b2f8b44e500

@sandra

I want to do the same. Have you a solution that works ?

did you look at @nlecaude suggestion?

I can post something if you still need help. How many rows? And columns? What will you do if the rows gets too much, do you want it to paginate or do you want a limiter to only show the last x amnt of rows?

@IoTPlay

That would be helpful.
It would be a table with 3 columns with 10 rows. After the 10 lines, the oldest messages are to be thrown away, which are then no longer interesting.

What are the columns? Is one a timestamp? Do you trust the order you send in, or do you want it ordered as well? What do you want to sort on?

I will build you a mockup, will use JS in a function node to push the latest json object into a flow context array, after retrieving the full array from flow context, use JSONata in a Change node to sort the array, use a function node to build the html for the table, then push that into a Dashboard template node...

The columns are: Message, Timestamp, Confirmed
I trust the order

that sounds good

One more thing, does the new messages come in one by one, or in an array of more than one at a time?

they come one by one

Here you go..., either on flows.nodered.org - Example of a scrolling HTML Table based on incoming JSON objects, or below:

[{"id":"d09ed06f.f25cd8","type":"inject","z":"d12440d2.b53e4","name":"msg1","topic":"","payload":"{\"msg\":\"Message one, bla bla\",\"confirm\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":120,"wires":[["e8660945.b30728"]]},{"id":"e8660945.b30728","type":"function","z":"d12440d2.b53e4","name":"add ts","func":"js_obj      = msg.payload;\n\nvar d       = new Date();\nvar jstime  = d.getTime();\n\njs_obj.timestamp = jstime;\n\nmsg.payload = js_obj ;\n\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":120,"wires":[["a8b5a5fc.2aead8"]]},{"id":"f16f6ae2.920e08","type":"inject","z":"d12440d2.b53e4","name":"msg2","topic":"","payload":"{\"msg\":\"Message two, blip bloep\",\"confirm\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":160,"wires":[["e8660945.b30728"]]},{"id":"78728c52.914c5c","type":"inject","z":"d12440d2.b53e4","name":"msg3","topic":"","payload":"{\"msg\":\"Message three, cat fish\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":200,"wires":[["e8660945.b30728"]]},{"id":"9cbe3b4c.ee23a","type":"inject","z":"d12440d2.b53e4","name":"msg4","topic":"","payload":"{\"msg\":\"Message four, send it !\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":240,"wires":[["e8660945.b30728"]]},{"id":"8868b533.a0ca58","type":"inject","z":"d12440d2.b53e4","name":"msg5","topic":"","payload":"{\"msg\":\"Message five, my crazy\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":280,"wires":[["e8660945.b30728"]]},{"id":"4606c10e.e86af","type":"inject","z":"d12440d2.b53e4","name":"msg6","topic":"","payload":"{\"msg\":\"Message six, whoop whoop !\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":320,"wires":[["e8660945.b30728"]]},{"id":"d45c309b.ff244","type":"inject","z":"d12440d2.b53e4","name":"msg7","topic":"","payload":"{\"msg\":\"Message seven\",\"confirm\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":360,"wires":[["e8660945.b30728"]]},{"id":"fafc9872.c01ad8","type":"inject","z":"d12440d2.b53e4","name":"msg8","topic":"","payload":"{\"msg\":\"Message 8, whats up monkey?\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":400,"wires":[["e8660945.b30728"]]},{"id":"7db5cc27.a52cc4","type":"inject","z":"d12440d2.b53e4","name":"msg9","topic":"","payload":"{\"msg\":\"Message 9, fly away\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":440,"wires":[["e8660945.b30728"]]},{"id":"a88ab58a.965558","type":"inject","z":"d12440d2.b53e4","name":"msg10","topic":"","payload":"{\"msg\":\"Message 10, diving for gold\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":480,"wires":[["e8660945.b30728"]]},{"id":"1eb4fe02.43365a","type":"inject","z":"d12440d2.b53e4","name":"msg11","topic":"","payload":"{\"msg\":\"Message 11, flying fish\",\"confirm\":1}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":520,"wires":[["e8660945.b30728"]]},{"id":"75e6eb01.d665a4","type":"inject","z":"d12440d2.b53e4","name":"msg12","topic":"","payload":"{\"msg\":\"Message 12, octopus\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":560,"wires":[["e8660945.b30728"]]},{"id":"38fd638e.ff702c","type":"inject","z":"d12440d2.b53e4","name":"msg13","topic":"","payload":"{\"msg\":\"Message 13, swim away\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":600,"wires":[["e8660945.b30728"]]},{"id":"462e1742.f231c8","type":"inject","z":"d12440d2.b53e4","name":"msg14","topic":"","payload":"{\"msg\":\"Message 14, warra warra\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":640,"wires":[["e8660945.b30728"]]},{"id":"76b38f5.74467f","type":"inject","z":"d12440d2.b53e4","name":"msg15","topic":"","payload":"{\"msg\":\"Message 15, fish paste\",\"confirm\":0}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":680,"wires":[["e8660945.b30728"]]},{"id":"f40b6b74.a253d","type":"template","z":"d12440d2.b53e4","name":"html","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<table border=\"1\">\n    \n    \n    <thead>\n        <tr>\n            <th colspan=\"3\">{{topic}}</th>\n        </tr>\n    </thead>\n    \n    \n    <tr>\n        <th>Message</th>\n        <th>Confirm</th>\n        <th>timestamp</th>\n\n    </tr>\n    {{#payload}}\n        <tr class=\"\">\n            <td>{{msg}}</td>            \n            <td>{{confirm}}</td>\n            <td>{{timestamp}}</td>\n        </tr>\n    {{/payload}}\n</table>\n","output":"str","x":730,"y":120,"wires":[["f94cd310.5d308"]]},{"id":"f94cd310.5d308","type":"ui_template","z":"d12440d2.b53e4","group":"ce913522.c3fc68","name":"Scrolling Messages","order":0,"width":0,"height":0,"format":"<div ng-bind-html=\"msg.payload\" height=\"500\" style=\"height: 350px;\"></div>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":970,"y":120,"wires":[[]]},{"id":"a8b5a5fc.2aead8","type":"function","z":"d12440d2.b53e4","name":"Cr/Upd msg_events","func":"var msg_obj       = msg.payload ;\nvar arr_msgs = flow.get(\"msg_events\", 'memoryOnly');\n\nif (arr_msgs===undefined ) {\n    // Create an empty array if it does not exist yet\n    arr_msgs = [];\n    //arr_msgs.push(msg_obj) ; \n        if (msg_obj !== \"1\") {\n            arr_msgs.push(msg_obj);\n            flow.set(\"msg_events\",arr_msgs, 'memoryOnly');\n        }\n    \n//    return msg ;\n\n} else {\n        // This is a new user, save and return in the first port\n        if (msg_obj !== \"1\") {\n            arr_msgs.push(msg_obj);\n            flow.set(\"msg_events\",arr_msgs, 'memoryOnly');\n        }\n} \nmsg.payload = flow.get(\"msg_events\", 'memoryOnly');\nreturn msg;\n","outputs":1,"noerr":0,"x":400,"y":180,"wires":[["dfc225a0.c8d0f","1361dc1c.332194"]]},{"id":"dfc225a0.c8d0f","type":"debug","z":"d12440d2.b53e4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":690,"y":300,"wires":[]},{"id":"6d22885b.4c2138","type":"function","z":"d12440d2.b53e4","name":"zap msg_events flow context","func":"var cfg = undefined ;\nflow.set('msg_events', cfg, 'memoryOnly');\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":40,"wires":[[]]},{"id":"7faf1d4.0aff2e4","type":"inject","z":"d12440d2.b53e4","name":"","topic":"","payload":"1","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"0.1","x":90,"y":40,"wires":[["6d22885b.4c2138"]]},{"id":"f2a75771.ec8fc","type":"function","z":"d12440d2.b53e4","name":"topic & 10","func":"var arr = msg.payload ;\nvar i   = 10;\nif(typeof arr === undefined) {\n    return msg;\n} else {\n    msg.payload = arr.slice(0, 10);    \n    msg.topic = 'The Last Ten Messages :';\n    return msg;\n}","outputs":1,"noerr":0,"x":650,"y":180,"wires":[["f40b6b74.a253d"]]},{"id":"1361dc1c.332194","type":"change","z":"d12440d2.b53e4","name":"sort","rules":[{"t":"set","p":"payload","pt":"msg","to":"($sort(payload,function($l , $r){$l.timestamp < $r.timestamp }) )","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":120,"wires":[["f2a75771.ec8fc"]]},{"id":"ce913522.c3fc68","type":"ui_group","z":"","name":"Test Messages","tab":"619caa7a.fef80c","disp":true,"width":"9","collapse":false},{"id":"619caa7a.fef80c","type":"ui_tab","z":"","name":"Test","icon":"dashboard"}]

JĂ©an.

3 Likes

Thanks for your work!

I still have a question, I get over MQTT the msg.payload which I want to write in the column "Message". I have to add the confirmation information in a function node and output both as a JSON object and pass to the "add ts" function, how do I do that?

ok that with the MQTT messages I have managed, but I have misunderstood the timestamp. I would like to give the current time there. But do not get it to change this

Very nice @IoTPlay!

Hello @Georg25, I am glad you solved bringing the two json objects together. (or a message and the confirmation.)

Gegarding your 2nd message, I just added the timstamp for demo- and sorting purpose. You need to convert the timestamp to a format of your choice, I could show if you do not know how to. What would you like the format to be? i.e. yy-mm-dd hh:mm ?

@IoTPlay

That would be great, my preferred format would be hh: mm, dd.mm.yy

Thanks

Georg

Updated with date & time in the html table. I changed the html template node, for the table, and the add ts node. Depending on your timezone you will have to make changes here.

Updated in flow.nodered.org

@IoTPlay

Thanks