Node-Red using chart.js vertical bar graph

Hi ,

I am fairly new to Node-Red but think it is a great tool. Once you get the hang of using messages . . . it is amazing!

I have created charts in Node-Red using the node-red-contrib-chartjs. The charts I have created are more complicated than this example, but I am really stuck with this simple bar chart.

(This is my first post. I hope I am inserting code and pictures properly in this forum - so please let me know if I am not).

Below is the chart I am trying to create along with the flow.

image image

I have created a simplified flow to show the problem. In the first node I generate a simple payload. The second moustache template node runs the chart.js script and the 3rd node sends it to the dashboard.

The problem is with the second node and how it "reads" the payload. When I "hardcode" the "labels" and "data" it works fine. However when I try to read this info from the payload, it does not work (currently commented out). I realize that this is quite simple, but I just can't figure it out? Other more complicated charts work very well! I also realize that I can use the the built in Node-Red charts, but all the other more complicated charts already use the node-red-contrib-chartjs, so I would really like to get this to work with chart.js and understand what I am missing here. Thanks for your help!

[{"id":"54df4d0a.317e64","type":"ui_template","z":"f341b612.8a1718","group":"90d07240.50d4f","name":"OEE Bar Chart (dynamic)","order":3,"width":"6","height":"5","format":"This will be ignored","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":910,"y":280,"wires":[[]]},{"id":"e80d0b3f.377338","type":"function","z":"f341b612.8a1718","name":"OEE BarChtCalc","func":"\n\nmsg.payload = { \n                data: [20.1, 40.2, 55.9, 98.3],\n                labels: ['\"Avail\"', '\"Perf\"', '\"Qual\"', '\"OEE\"']};\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":280,"wires":[["f0d60e98.1757e","b0cd2350.7b882"]]},{"id":"f0d60e98.1757e","type":"debug","z":"f341b612.8a1718","name":"Msg00","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":630,"y":240,"wires":[]},{"id":"b0cd2350.7b882","type":"template","z":"f341b612.8a1718","name":"OEE Bar Cht Template","field":"template","fieldType":"msg","format":"html","syntax":"mustache","template":"<canvas id=\"barOEE\" width=\"1\" height=\"1\"></canvas>\n\n<script>\nnew Chart(document.getElementById(\"barOEE\"), {\n    type: \"bar\",\n    data: {\n        //labels: [{{{msg.payload.labels}}}],\n        labels: [\"Avail\", \"Perf\", \"Qual\", \"OEE\"],\n        datasets: \n            [{\n            //data: [{{{msg.payload.data}}}],\n            data: [10.2,20.4,50.3,92.9],\n            backgroundColor: 'rgba(0,0,255,1)'\n            //borderColor: 'rgba(0,0,255,1)',\n            //borderWidth: 0,\n            //hoverBackgroundColor: 'rgba(0,255,0,1)',\n            //hoverBorderColor: 'rgba(0,255,0,1)'\n           \t}]\n        },\n \n    options: {\n        animation: {duration: 0},\n          \n        title: {\n            responsive: true,\n            display: true,\n            text: \"OEE Chart (%)\",\n            fontColor: 'rgb(255,255,255)'\n            },\n            \n        legend: {\n            display: false,\n            },\n            \n        maintainAspectRatio: false,\n    \n        scales: {\n            yAxes: [{\n                stacked: true,\n                gridLines: {\n                    display: true\n                },\n            ticks: {\n                beginAtZero: true,\n                fontColor: 'white'\n                }\n            }],\n        \n            xAxes: [{\n                stacked: true,\n                gridLines: {\n                    display: false\n                },\n                ticks: {\n                  fontColor: 'white',\n                  autoSkip: false\n                }\n            }]\n        }  \n        \n   }  \n\n});\n</script>","output":"str","x":680,"y":280,"wires":[["54df4d0a.317e64"]]},{"id":"9e63afbd.52e0f","type":"inject","z":"f341b612.8a1718","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":280,"wires":[["e80d0b3f.377338"]]},{"id":"90d07240.50d4f","type":"ui_group","z":"","name":"Test Space","tab":"49f2883.aa81678","order":1,"disp":true,"width":"6","collapse":false},{"id":"49f2883.aa81678","type":"ui_tab","z":"","name":"Other Line","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Hi @Curious, welcome to the forum.

So what you want is to use properties from the message inside a script.

I would recommend to have a look in the answer in this Stack Overflow post.

In the meantime I will see if I can change your flow to make it easier for you to understand.

Edit: I did it quickly. I let you test it better.

[{"id":"e95ac189.3d8bd","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"a6fcf00b.25442","type":"ui_template","z":"e95ac189.3d8bd","group":"6709e4ca.80379c","name":"OEE Bar Chart (dynamic)","order":3,"width":"6","height":"5","format":"This will be ignored","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":870,"y":220,"wires":[[]]},{"id":"b94f05ee.18d818","type":"function","z":"e95ac189.3d8bd","name":"OEE BarChtCalc","func":"\n\nmsg.payload = { \n                data: [20.1, 40.2, 55.9, 98.3],\n                labels: ['\"Avail\"', '\"Perf\"', '\"Qual\"', '\"OEE\"']};\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":220,"wires":[["d01aed9f.82142","463b3e5f.978a2"]]},{"id":"d01aed9f.82142","type":"debug","z":"e95ac189.3d8bd","name":"Msg00","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":590,"y":180,"wires":[]},{"id":"463b3e5f.978a2","type":"template","z":"e95ac189.3d8bd","name":"OEE Bar Cht Template","field":"template","fieldType":"msg","format":"html","syntax":"mustache","template":"<canvas id=\"barOEE\" width=\"1\" height=\"1\"></canvas>\n\n<script>\n    (function(scope) {\n        scope.$watch('msg', function(msg) {\n\n\nnew Chart(document.getElementById(\"barOEE\"), {\n    type: \"bar\",\n    data: {\n        labels: msg.payload.labels,\n        labels: [\"Avail\", \"Perf\", \"Qual\", \"OEE\"],\n        datasets: \n            [{\n            data: msg.payload.data,\n            //data: [10.2,20.4,50.3,92.9],\n            backgroundColor: 'rgba(0,0,255,1)'\n            //borderColor: 'rgba(0,0,255,1)',\n            //borderWidth: 0,\n            //hoverBackgroundColor: 'rgba(0,255,0,1)',\n            //hoverBorderColor: 'rgba(0,255,0,1)'\n           \t}]\n        },\n \n    options: {\n        animation: {duration: 0},\n          \n        title: {\n            responsive: true,\n            display: true,\n            text: \"OEE Chart (%)\",\n            fontColor: 'rgb(255,255,255)'\n            },\n            \n        legend: {\n            display: false,\n            },\n            \n        maintainAspectRatio: false,\n    \n        scales: {\n            yAxes: [{\n                stacked: true,\n                gridLines: {\n                    display: true\n                },\n            ticks: {\n                beginAtZero: true,\n                fontColor: 'white'\n                }\n            }],\n        \n            xAxes: [{\n                stacked: true,\n                gridLines: {\n                    display: false\n                },\n                ticks: {\n                  fontColor: 'white',\n                  autoSkip: false\n                }\n            }]\n        }  \n        \n   }  \n\n});\n\n        });\n    })(scope);\n</script>","output":"str","x":640,"y":220,"wires":[["a6fcf00b.25442"]]},{"id":"94a9ba05.7b9bc8","type":"inject","z":"e95ac189.3d8bd","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":220,"wires":[["b94f05ee.18d818"]]},{"id":"6709e4ca.80379c","type":"ui_group","z":"","name":"Test Space","tab":"4ac040fe.e117b","order":1,"disp":true,"width":"6","collapse":false},{"id":"4ac040fe.e117b","type":"ui_tab","z":"","name":"Other Line","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Hi Andrei,

Thank you for this incredibly fast answer. I have tried the sample and it works (although I still need to test it in my app). To be honest, I don't really understand why it works?

After your update, I did find this link by searching for: "node-red scope watch":
https://flows.nodered.org/flow/2f1aaf0635f9bf23207152682323240a

I had a quick read, (I will read it again), but I cannot say that I fully understand it. Are there any other java script of Node-Red references that would be helpful?

Also, why do all my other charts display correctly using the triple braces (as was initially provided in the sample). Should I be changing all the other templates nodes in my application?

Hi Andrei,

As a follow up, if I understand this correctly, you created a function called scope. Inside the function scope you put a $watch(object, handler). If object (msg) changes, it will call the handler which will run the rest of the script. Is this correct?

Just to confirm, is scope a reserved word, or could could you have called this something else?

Also, at the end of the function you added "(scope)". Sorry for my poor java script knowledge, but is this just a "comment" after the brackets to show what the brackets are associated with or is "scope" a function which will be executed one more time?

Ouch! :smile: Don't worry, actually I doubt that I understood it fully then either and since then I gave up using Angular as it was causing me far too much existential angst (er, or something like that I'm sure).

Actually you are creating a little bubble and picking up the Angular scope object inside the bubble. And you are completely right, Angular itself creates the scope object. This is completely hidden by the Dashboard code though which is why it looks odd.

Just bear in mind that, when getting down and dirty with Dashboard, you are no longer just dealing with Node-RED, you are now working with the front-end (browser client).

So it isn't just a case of working with Node-RED - which is easy(ish) to understand. It is working with Anglular v1 wrapped inside a complex set of code (Dashboard) that makes things easy for you.

But easy only goes so far. That is why uibuilder was born.

2 Likes

Hi @Curious

Firstly it is important to realize (something that you probably already knows) that our Node-RED flow is code that runs under a Node.JS application (Node-RED runtime). On the other hand the JavaScript code inside a ui_template node is code that run in the browser (frontend). Often you want to send data from one side (backend) to the frontend. In Node-RED dashboard (ui_template is a widget in the dashboard) this data exchange is done with help of Angular framework (and websockets). So, the point is that to fully master the dashboard is is good to know Angular (which is not my case :astonished: ) but I can say that the $scope is an object provided by Angular whereas $watch is a method of this object. A good reference for $watch is the one below.

Now when you build an html document using the core template node (not the ui_template) you can use mustache syntax for text substitution. It will work for everything html that is outside the script tags. You can also use CSS variables in the core template node. I use it frequently to style html using properties from the message payload. In such case there is no need to use the Angular scope$watch.

1 Like

Just to quickly update this thread in case someone wants to follow this in the future: Andrei's solution works. (Note also that with his solution I no longer need to embed an extra quote around strings to prevent them from being stripped off, i.e. '"OEE"' vs simply "OEE").

I think the explanations in the threads have helped me to better understand what is happening. The reason I kept investigating is because I couldn't figure out why all the other charts were working.

I finally noticed that in my template node, I was using "msg.payload.labels" vs "payload.labels" that I used in other places. I believe that if the template node property is setup with "property" as "msg.template" it will basically do the same as the "function scope" code that Andrei has provided, but he will have to confirm.

As a result, I changed my original code from "msg.payload.labels" vs "payload.labels" and it seems to work.

Thank you very much!

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