# Scrolling Text box on dashboard?

**URL:** <https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788>\
**Category:** Dashboard\
**Created:** [3 September 2018 01:22 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788 "2018-09-03T01:22:19Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![craigcurtin](https://avatars.discourse-cdn.com/v4/letter/c/94ad74/32.png) [@craigcurtin](https://discourse.nodered.org/u/craigcurtin)\
**Post date:** [3 September 2018 01:22 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/1 "2018-09-03T01:22:19Z")

</div>

Guys,

Wondering if anyone knows a way to implement a scrolling text box in Dashboard ? Lets say something like 30 chars wide by 10 lines deep, everytime i add a new line it is added at the top and all other lines scroll down with the bottom line dropping off ? (of course it could work the other way also such as Tailing a Linux file etc)

regards

Craig

---

<div class="post-metadata">

**Author:** ![Andrei](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/andrei/32/10446_2.png) [@Andrei](https://discourse.nodered.org/u/Andrei)\
**Post date:** [3 September 2018 04:35 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/2 "2018-09-03T04:35:48Z")

</div>

As usual with Node-RED there are many possible ways to achieve what you want. Considering that you want a table with very few lines and, more important, with a fixed amount of lines (only 10 lines) then I would do it with SVG text in a UI template node. An auxiliary function would manage an array of elements and have them stored in the flow context.

 ![node-red-06](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/d/d477cd4d05ce506b3d29adb7a5bd8f9a5a71bb16.gif)

Flow:

```auto
[{"id":"594506cc.51e408","type":"tab","label":"Scrolling text ","disabled":false,"info":""},{"id":"f26df972.5d3ae8","type":"ui_template","z":"594506cc.51e408","group":"1ebe6305.3aa6ad","name":"SVG based template","order":0,"width":"6","height":"6","format":"<style>\n\n #tex1 {\n font-weight:bolder;\n font-size: 12;\n letter-spacing: 4px;\n fill: white;\n }\n \n</style>\n\n\n<svg height=\"300\" width=\"300\" >\n\n<text id=\"tex1\" x=\"10\" y=\"30\" > {{msg.payload[0]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"50\" > {{msg.payload[1]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"70\" > {{msg.payload[2]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"90\" > {{msg.payload[3]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"110\" > {{msg.payload[4]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"130\" > {{msg.payload[5]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"150\" > {{msg.payload[6]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"170\" > {{msg.payload[7]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"190\" > {{msg.payload[8]}} </text>\n<text id=\"tex1\" x=\"10\" y=\"210\" > {{msg.payload[9]}} </text>\n</svg>\n\n","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":760,"y":380,"wires":[[]]},{"id":"3438664c.1d39ba","type":"function","z":"594506cc.51e408","name":"Create table in context","func":"let sctab = [];\nflow.set(\"sctab\", sctab);\nreturn msg;","outputs":"1","noerr":0,"x":400,"y":220,"wires":[[]]},{"id":"6b543271.7a47fc","type":"inject","z":"594506cc.51e408","name":"","topic":"","payload":"Start","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":"5","x":150,"y":220,"wires":[["3438664c.1d39ba"]]},{"id":"67195cf7.a5ea24","type":"function","z":"594506cc.51e408","name":"Add word to scrolling table","func":"let pay = msg.payload;\n\n// Read scrolling table from context\nlet sctab = flow.get(\"sctab\");\n\n// Modify scrolling table by adding msg.payload as first element\nlet size = sctab.unshift(pay);\n\n// Remove last element from scrolling table\nif (size >9) sctab.pop();\n\n// Update context for scrolling text\nflow.set(\"sctab\",sctab);\n\n// Shalow copy updated scrolling table to msg.payload\nmsg.payload = [...sctab];\n\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":380,"wires":[["f26df972.5d3ae8"]]},{"id":"e4b42f5e.2458c","type":"inject","z":"594506cc.51e408","name":"","topic":"","payload":"Alfa","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":340,"wires":[["67195cf7.a5ea24"]]},{"id":"d5d60837.4cadd8","type":"inject","z":"594506cc.51e408","name":"","topic":"","payload":"Bravo","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":380,"wires":[["67195cf7.a5ea24"]]},{"id":"79f8982e.19f1d8","type":"inject","z":"594506cc.51e408","name":"","topic":"","payload":"Charlie","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":420,"wires":[["67195cf7.a5ea24"]]},{"id":"68b03084.d691","type":"inject","z":"594506cc.51e408","name":"","topic":"","payload":"Delta","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":460,"wires":[["67195cf7.a5ea24"]]},{"id":"1ebe6305.3aa6ad","type":"ui_group","z":"","name":"LAB","tab":"2d3c18ea.7ea3d8","order":3,"disp":false,"width":"6","collapse":false},{"id":"2d3c18ea.7ea3d8","type":"ui_tab","z":"","name":"Table","icon":"dashboard"}]

```

---

<div class="post-metadata">

**Author:** ![craigcurtin](https://avatars.discourse-cdn.com/v4/letter/c/94ad74/32.png) [@craigcurtin](https://discourse.nodered.org/u/craigcurtin)\
**Post date:** [3 September 2018 06:42 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/3 "2018-09-03T06:42:54Z")

</div>

You are the man Andrei !!!

That is fantastic thanks - almost perfectly what i want !

regards

Craig

---

<div class="post-metadata">

**Author:** ![Andrei](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/andrei/32/10446_2.png) [@Andrei](https://discourse.nodered.org/u/Andrei)\
**Post date:** [3 September 2018 12:57 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/4 "2018-09-03T12:57:55Z")

</div>

Good to know it helped. If you see any opportunity to improve the code or restyle the dashboard for better please let me know. Perhaps if it is improved I can make it available in the flow library.

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [3 September 2018 13:08 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/5 "2018-09-03T13:08:57Z")

</div>

Let's make less code lines with usage of **ng-repeat**

`<svg height="300" width="300" > <text id="tex1" ng-repeat="line in msg.payload track by $index" x="10" ng:attr:y="{{$index * 20}}" > {{line}} </text> </svg>`

🙂

---

<div class="post-metadata">

**Author:** ![craigcurtin](https://avatars.discourse-cdn.com/v4/letter/c/94ad74/32.png) [@craigcurtin](https://discourse.nodered.org/u/craigcurtin)\
**Post date:** [4 September 2018 00:38 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/6 "2018-09-04T00:38:14Z")

</div>

Will do - but i think it will be a long time until i can improve on your code !!

regards

Craig

---

<div class="post-metadata">

**Author:** ![craigcurtin](https://avatars.discourse-cdn.com/v4/letter/c/94ad74/32.png) [@craigcurtin](https://discourse.nodered.org/u/craigcurtin)\
**Post date:** [4 September 2018 00:39 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/7 "2018-09-04T00:39:26Z")

</div>

Thanks for that hotNipi - i am sure that is more elegant and tidier - but as a "babe in the woods" i find Andrei's is easier for me to follow and understand.

regards

Craig

---

<div class="post-metadata">

**Author:** ![kebebs](https://avatars.discourse-cdn.com/v4/letter/k/3be4f8/32.png) [@kebebs](https://discourse.nodered.org/u/kebebs)\
**Post date:** [5 September 2018 14:01 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/8 "2018-09-05T14:01:47Z")

</div>

I have just used your code and it works but it keeps cutting out part of the first payload... the full text is not shown, only parts of the string come up on the dashboard. like the cuts of some part of the string.. please do you know why this is

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [5 September 2018 14:28 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/9 "2018-09-05T14:28:58Z")

</div>

Yep. lets shift textlines down a bit  
`<svg height="300" width="300" > <text id="tex1" ng-repeat="line in msg.payload track by $index" x="10" ng:attr:y="{{($index * 20)+20}}" > {{line}} </text> </svg>`

---

<div class="post-metadata">

**Author:** ![kebebs](https://avatars.discourse-cdn.com/v4/letter/k/3be4f8/32.png) [@kebebs](https://discourse.nodered.org/u/kebebs)\
**Post date:** [5 September 2018 14:31 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/10 "2018-09-05T14:31:30Z")

</div>

Thank you soo mucch... that works.. youre a genius :DD

---

<div class="post-metadata">

**Author:** ![hotNipi](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hotnipi/32/383_2.png) [@hotNipi](https://discourse.nodered.org/u/hotNipi)\
**Post date:** [5 September 2018 14:35 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/11 "2018-09-05T14:35:12Z")

</div>

Oo no I'm not. It's just html 😃

---

<div class="post-metadata">

**Author:** ![abra](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/abra/32/3443_2.png) [@abra](https://discourse.nodered.org/u/abra)\
**Post date:** [4 February 2019 13:16 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/12 "2019-02-04T13:16:22Z")

</div>

> Considering that you want a table with very few lines and, more important, with a fixed amount of lines (only 10 lines) then I would do it with SVG text in a UI template node

@Andrei : Do you suggest that for other scenarios another approach would be better suitable? I have an application where the lines in the text box are created dynamically (kind of log messages received asynchronously via mqtt, generated more or less continuously). I would limit the number of lines to be displayed at for example 100, and practically it should be possible to scroll the laast 100 lines stored in a ring buffer.

---

<div class="post-metadata">

**Author:** ![Andrei](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/andrei/32/10446_2.png) [@Andrei](https://discourse.nodered.org/u/Andrei)\
**Post date:** [4 February 2019 16:04 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/13 "2019-02-04T16:04:03Z")

</div>

Hi @abra, I don't know how to do it with SVG (used in previous flow) as this would require complex handling of x, y positions. It's is not difficult to do something with the template node though. I just drafted a solution that seems to work (not thoroughly tested).

 ![s-01](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/c/cf2c191fda91228a6eeb8e3796c4014772e54a33.png)

You will need to install `node-red-contrib-ring-buffer`, which is a fine node.

In this flow I use the moment.js library to generate fake time and some additional nodes to generate a fake list of events. The template node expects msg.payload to be an object with the properties time and event but, of course, you will modify this (and everything else) to suit your needs.

Flow:

````auto
[{"id":"f6b18fb3.2e17c","type":"tab","label":"Attempt Scrolling widget","disabled":false,"info":"https://groups.google.com/d/topic/node-red/oJyDSrXHvpg/discussion\n\n\n\n```\n {\n \"building\": \"home\",\n \"room\": \"bedroom\",\n \"device\": \"light\",\n \"tech\": \"X10\",\n \"id\": \"C5\",\n \"type\": \"actuator\",\n \"desc\": \"x10 device and lamp\"\n },\n {\n \"building\": \"home\",\n \"room\": \"office\",\n \"device\": \"light\",\n \"tech\": \"X10\",\n \"id\": \"C6\",\n \"type\": \"actuator\",\n \"desc\": \"x10 device and lamp\"\n }\n```\n\n![template-view-object](/nri/template-view-object.png)\n"},{"id":"71c11480.98778c","type":"template","z":"f6b18fb3.2e17c","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html>\n <head></head>\n <body>\n <ul>\n {{#payload}}\n <li>{{time}}, {{event}}</li>\n {{/payload}}\n </ul>\n</body>\n</html>","x":620,"y":180,"wires":[["b6a83bc0.08c928"]]},{"id":"b6a83bc0.08c928","type":"ui_template","z":"f6b18fb3.2e17c","group":"435d299e.91f718","name":"","order":0,"width":"12","height":"3","format":"<style>\n\n #tex1 {\n font-weight:bolder;\n font-family: Menlo, monospace;\n font-size: 5;\n letter-spacing: 4px;\n color: lime;\n }\n \n</style>\n<div ng-bind-html=\"msg.payload\" id=\"tex1\"></div>\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":780,"y":180,"wires":[[]]},{"id":"f0a87718.580bb8","type":"comment","z":"f6b18fb3.2e17c","name":"Attempt to create a scrolling widget","info":"","x":180,"y":100,"wires":[]},{"id":"d9b9a91b.d40098","type":"ring-buffer","z":"f6b18fb3.2e17c","name":"","capacity":16,"order":"old-to-new","sendOnlyIfFull":false,"pushAfterClear":false,"extra":false,"x":460,"y":180,"wires":[["71c11480.98778c"]]},{"id":"f1f3829a.64333","type":"inject","z":"f6b18fb3.2e17c","name":"","topic":"","payload":"","payloadType":"date","repeat":"2","crontab":"","once":true,"onceDelay":"5","x":130,"y":180,"wires":[["4722a34d.ba57bc"]]},{"id":"4722a34d.ba57bc","type":"function","z":"f6b18fb3.2e17c","name":"Random object","func":"let mRandom = msg.payload % 26;\nlet moment = global.get('moment'); \nlet tim = moment().format('MMMM Do YYYY, h:mm:ss a');\n\nmsg.payload = {\"time\" : tim, \"event\" : flow.get(\"fakeEvents\")[mRandom]};\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":180,"wires":[["d9b9a91b.d40098"]]},{"id":"1238b2c5.173cbd","type":"function","z":"f6b18fb3.2e17c","name":"Create fake events","func":"\nlet words = [\n \"Alfa\",\n \"Bravo\",\n \"Charlie\", \n \"Delta\",\n \"Echo\",\n \"Foxtrot\",\n \"Golf\",\n \"Hotel\",\n \"India\",\n \"Juliett\", \n \"Kilo\", \n \"Lima\", \n \"Mike\", \n \"November\", \n \"Oscar\", \n \"Papa\", \n \"Quebec\", \n \"Romeo\", \n \"Sierra\", \n \"Tango\", \n \"Uniform\", \n \"Victor\", \n \"Whiskey\", \n \"X-ray\", \n \"Yankee\", \n \"Zulu\"\n \n];\n\nflow.set(\"fakeEvents\", words);\n\nmsg.payload = words;\nreturn msg;","outputs":"1","noerr":0,"x":390,"y":280,"wires":[["1c76d58b.f2018a"]]},{"id":"496927ba.28ba08","type":"inject","z":"f6b18fb3.2e17c","name":"","topic":"","payload":"Start","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":"0.1","x":130,"y":280,"wires":[["1238b2c5.173cbd"]]},{"id":"1c76d58b.f2018a","type":"debug","z":"f6b18fb3.2e17c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":580,"y":280,"wires":[]},{"id":"435d299e.91f718","type":"ui_group","z":"","name":"Group 1","tab":"7ac377c5.e6ac08","disp":true,"width":"12","collapse":false},{"id":"7ac377c5.e6ac08","type":"ui_tab","z":"","name":"Tab1","icon":"dashboard"}]

````

---

<div class="post-metadata">

**Author:** ![Andrei](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/andrei/32/10446_2.png) [@Andrei](https://discourse.nodered.org/u/Andrei)\
**Post date:** [4 February 2019 20:07 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/14 "2019-02-04T20:07:54Z")

</div>

I forgot that there is a much better looking solution in Node-RED library:

[https://flows.nodered.org/flow/6f164cfd4b548d603c7387b29ed54027](https://flows.nodered.org/flow/6f164cfd4b548d603c7387b29ed54027)

---

<div class="post-metadata">

**Author:** ![abra](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/abra/32/3443_2.png) [@abra](https://discourse.nodered.org/u/abra)\
**Post date:** [5 February 2019 15:19 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/15 "2019-02-05T15:19:30Z")

</div>

Thaks for your sample and for the link. I probably need a combination of both, as the HTML scroll sample does not have a vertical scroll bar to see the older messages. I also have to search a sample about how to select a line from the table by double-clicking it.  
Would be possible to open a "detailed view" when a line from the table is double-clicked, and to pass the context (the message payload for the line) to it?

---

<div class="post-metadata">

**Author:** ![Andrei](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/andrei/32/10446_2.png) [@Andrei](https://discourse.nodered.org/u/Andrei)\
**Post date:** [5 February 2019 17:42 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/16 "2019-02-05T17:42:28Z")

</div>

> [@abra](#):
>
> Would be possible to open a "detailed view" when a line from the table is double-clicked, and to pass the context (the message payload for the line) to it?

It would be necessary a good amount of time and effort to devolop a widget with such capability. This would require an experienced front end developer and possibly additional libraries being added to the project. Perhaps a better approach is using Node-RED + influx stack (db + other tools).

---

<div class="post-metadata">

**Author:** ![abra](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/abra/32/3443_2.png) [@abra](https://discourse.nodered.org/u/abra)\
**Post date:** [15 February 2019 10:02 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/17 "2019-02-15T10:02:39Z")

</div>

Is it possible to use bootstrap in a node red ui\_template ? If yes, does anyone have a sample or a link to some sample?

---

<div class="post-metadata">

**Author:** ![shrickus](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/shrickus/32/517_2.png) [@shrickus](https://discourse.nodered.org/u/shrickus)\
**Post date:** [16 February 2019 06:33 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/18 "2019-02-16T06:33:06Z")

</div>

> [@abra](#):
>
> Would be possible to open a "detailed view" when a line from the table is double-clicked, and to pass the context (the message payload for the line) to it?

Yes, you can use the Angular scope to send the data for each row of your table back to the downstream flow. [This discussion](https://discourse.nodered.org/t/access-variable-within-dashboard-html-template-sent-from-function/3725/12) has some examples of how to do that.

Put a debug node on the output of your `ui_template` node that builds the table, so you can see what is being passed back when you click the row. Once you have the right data being returned, you can pass that message to a `change` node to set the `msg.tab` property to be the "next page" to show, and send that to to a `ui_control` node, which then switches the dashboard to that page. At that point you just use the payload from the clicked table row to populate the widgets showing more details -- simple, eh? ;\*)

---

<div class="post-metadata">

**Author:** ![abra](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/abra/32/3443_2.png) [@abra](https://discourse.nodered.org/u/abra)\
**Post date:** [25 February 2019 16:57 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/19 "2019-02-25T16:57:04Z")

</div>

Thanks for your reply.  
How could be possible for the vertically scrollable table from the example above to automatically set the vertical scroll position to the newest entry, at the bottom of the table?

---

<div class="post-metadata">

**Author:** ![shrickus](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/shrickus/32/517_2.png) [@shrickus](https://discourse.nodered.org/u/shrickus)\
**Post date:** [25 February 2019 21:16 UTC](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788/20 "2019-02-25T21:16:21Z")

</div>

One technique is to have the table reverse sorted, so newest to oldest -- then any new rows will always be visible at the top of the page.

Another way would be to add a bit of javascript to the page, that gets the last row of the table and scrolls the browser to show it, using the [DOM scrollIntoView() method](https://www.w3schools.com/jsref/met_element_scrollintoview.asp).

[Next page](https://discourse.nodered.org/t/scrolling-text-box-on-dashboard/2788.md?page=2)
