This should be easy, but I cannot seem to figure out what I'm doing wrong here...

236 views
Skip to first unread message

Larry Roy

unread,
Sep 27, 2016, 8:15:37 PM9/27/16
to Node-RED
In a dashboard template node, this code works, and renders the value of the payload and a bar chart with one bar:

<h1>{{msg.payload}}</h1>
<canvas id="myBarChart" width="400" height="400"></canvas>
<script src="/Chart/dist/Chart.js"></script>
<script>
    var ctx = document.getElementById("myBarChart");
    var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['Item 1'],
            datasets: [
                {
                    type: 'bar',
                    label: 'Bar Component',
                    data: [25],
                }
            ]
        }
    });
</script>

The following code does not work, and I'm sure it's because I cannot access msg.payload using mustache formatting, but I can't figure out how to get the single value that is in the payload:

<h1>{{msg.payload}}</h1>
<canvas id="myBarChart" width="400" height="400"></canvas>
<script src="/Chart/dist/Chart.js"></script>
<script>
    var ctx = document.getElementById("myBarChart");
    var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['Item 1'],
            datasets: [
                {
                    type: 'bar',
                    label: 'Bar Component',
                    data: [{{msg.payload}}],
                }
            ]
        }
    });
</script>

How do I get msg.payload into the script?

Thanks for any ideas!

Colin Law

unread,
Sep 28, 2016, 7:01:46 AM9/28/16
to node...@googlegroups.com
1> How do I get msg.payload into the script?

My understanding (limited as it is) is that you cannot access the
message directly from javascript like that you have to do it as
described in http://flows.nodered.org/flow/2f1aaf0635f9bf23207152682323240a

Colin

>
> Thanks for any ideas!
>
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/7ed5a977-407f-4cec-9a4f-749787555e03%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Dave C-J

unread,
Sep 28, 2016, 8:48:03 AM9/28/16
to node...@googlegroups.com
​The dashboard template uses angular not moustache - so although it uses {{ a lot it's not the same :-(​

Rught now you would need to do that using a normal (non-ui) template node first then feed that into the ui-template to render.

Julian Knight

unread,
Sep 28, 2016, 6:57:25 PM9/28/16
to Node-RED
I think that you can access the msg object in the Angular (browser) script by accessing the "scope" variable?

The following works:

<div ng-bind-html="msg.payload"></div>
<script>
;(function(scope) {
   
//console.dir(scope.msg) // This doesn't work
    scope
.$watch('msg.payload', function() {
        console
.dir(scope.msg) // This DOES work
    })
})(scope)
</script>

Julian Knight

unread,
Sep 28, 2016, 7:10:38 PM9/28/16
to Node-RED
Oddly, that first console.dir should work because the following:

<div ng-bind-html="msg.payload"></div>
<script>
;(function(scope) {
   
//console.dir(scope.msg)

    console
.log('--- SCOPE ---')
    console
.dir(scope)
    scope
.$watch('msg.payload', function() {
        console
.log('- Scope.msg -')
        console
.dir(scope.msg)
       
//console.dir(msg)
   
})
})(scope)
</script>

Does show that scope contains scope.msg and that it contains the right data. However, outside the watch you would only recieve a single entry on startup, you wouldn't get more console output when you send a message into the template, only on startup (first page display).

When accessing via the watch, note that you get an undefined returned followed by the data, something to do with that way that Anglular sets itself up.

Julian Knight

unread,
Sep 28, 2016, 7:20:43 PM9/28/16
to Node-RED
Ah, I keep forgetting the way that the console var gets evaluated. The reason scope.msg appears in console.dir(scope) isn't that it actually exists when that command is run. Using the Chrome (rather than Firefox) dev console makes that clear.

Colin Law

unread,
Sep 29, 2016, 2:22:26 AM9/29/16
to node...@googlegroups.com
On 28 September 2016 at 23:57, Julian Knight <j.kni...@gmail.com> wrote:
> I think that you can access the msg object in the Angular (browser) script
> by accessing the "scope" variable?
>
> The following works:
>
> <div ng-bind-html="msg.payload"></div>
> <script>
> ;(function(scope) {
> //console.dir(scope.msg) // This doesn't work
> scope.$watch('msg.payload', function() {
> console.dir(scope.msg) // This DOES work
> })
> })(scope)
> </script>

I think that is the technique proposed in the link I posted.

Coincidentally I have been working on exactly the same problem - that
of using chart.js in a template node and providing it dynamic data.
This is what I have got so far, which does seem to work ok.

<script src="/Chart.bundle.min.js"></script>
<h3>Testing dynamic chart.js with Dashboard 2.0</h3>
<canvas id="myChart" width="50" height="50"></canvas>
<script>
var chartDef = {
type: 'line',
data: {
datasets: [{
label: "temperature",
fill: false,
lineTension: 0,
borderColor: "#00ff00",
pointRadius: 0,
pointBorderColor: "#ff0000",
pointBackgroundColor: "#0000ff",
borderWidth: 1,
data: [] // data is written here later
}, {
label: "setpoint",
fill: false,
lineTension: 0,
borderColor: "#ff0000",
pointRadius: 0,
pointBorderColor: "#ff0000",
pointBackgroundColor: "#ff0000",
borderWidth: 1,
data: [] // data is written here later
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
displayFormats: {
minute: 'hh:mm:ss',
}
}],
yAxes: [{
ticks: {
min: 10,
max: 25,
stepSize: 1
}
}]
},
animation: {
duration: 0
}
}
}

function doChart(dataPoints) {
var ctx = document.getElementById("myChart");
for (var i = 0; i < dataPoints.length; i++) {
chartDef.data.datasets[i].data = dataPoints[i];
}
var myChart = new Chart(ctx, chartDef);
};

(function(scope) {
scope.$watch('msg.payload', function(payload) {
if (payload) {
doChart(payload);
}
});
})(scope);
</script>

The above draws the chart as defined by chartDef (which is a line
chart with two lines) with the data points provided in msg.payload as
array of two arrays of data points. So something like
[[{x:1,y:1},{x:2,y:2}],[{x:1,y:4},{x:2,y:3}]].

I want to work refine it so that it does not recreate the chart every
time it gets a new payload but reuses the existing chart, just
updating the data it is displaying.

Colin

>
>
>
> On Wednesday, 28 September 2016 13:48:03 UTC+1, Dave C-J wrote:
>>
>> The dashboard template uses angular not moustache - so although it uses {{
>> a lot it's not the same :-(
>>
>> Rught now you would need to do that using a normal (non-ui) template node
>> first then feed that into the ui-template to render.
>
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/575e1592-28e4-4f84-922e-cc67b20ffdb8%40googlegroups.com.

Julian Knight

unread,
Sep 29, 2016, 4:04:25 AM9/29/16
to Node-RED
Ah, right - sorry thinking out loud.

Re the chart issue, you seem to be creating a new chart each time the page receives data. Could you not create the chart at start and simply add data to the array when you get an update?

Colin Law

unread,
Sep 29, 2016, 5:23:35 AM9/29/16
to node...@googlegroups.com
On 29 September 2016 at 09:04, Julian Knight <j.kni...@gmail.com> wrote:
> Ah, right - sorry thinking out loud.
>
> Re the chart issue, you seem to be creating a new chart each time the page
> receives data. Could you not create the chart at start and simply add data
> to the array when you get an update?

Yes, I am just working out how to do that. Just getting my head around
what is run in the server and what in the browser. I presume I have
just got to define the var myChart at the start of the script and then
in the update test the var for null. The docs for dynamically
updating with chart.js are pretty thin on the ground, or even
non-existent unless I am missing something, but I have found some
examples of how to do it. Will post the result when I have got it
working.

On a point of order I am used to inline/bottom posting, am I annoying
people by doing that on this list? I know some lists have strong
feelings on the issue and I don't want to antagonise anyone.

Colin

>
> On Thursday, 29 September 2016 07:22:26 UTC+1, Colin Law wrote:
>>
>>
>> I think that is the technique proposed in the link I posted.
>>
>> Coincidentally I have been working on exactly the same problem - that
>> of using chart.js in a template node and providing it dynamic data.
>> This is what I have got so far, which does seem to work ok.
>>
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/c2271c5d-dc1c-4749-9515-ad44763e946e%40googlegroups.com.

Colin Law

unread,
Sep 29, 2016, 6:35:54 AM9/29/16
to node...@googlegroups.com
This is it with re-use of the existing chart rather than creating it each time

<script src="/Chart.bundle.min.js"></script>
<h3>Testing dynamic scripts with Dashboard 2.0</h3>
var myChart = null;

function doChart(dataPoints) {
if (myChart) {
// chart already exists, update it
for (var i = 0; i < dataPoints.length; i++) {
myChart.data.datasets[i].data = dataPoints[i];
}
myChart.update();
} else {
// chart does not exist so load the data and create it
var ctx = document.getElementById("myChart");
for (var i = 0; i < dataPoints.length; i++) {
chartDef.data.datasets[i].data = dataPoints[i];
}
myChart = new Chart(ctx, chartDef);
}
};

(function(scope) {
scope.$watch('msg.payload', function(payload) {
if (payload) {
doChart(payload);
}
});
})(scope);
</script>

Colin

Julian Knight

unread,
Sep 29, 2016, 9:51:11 AM9/29/16
to Node-RED
Yes. The $watch function will run every time msg is updated so it is easy to catch. That includes an initial run on startup (that's startup of the UI page in a new browser session client side). On the initial run, you can expect the scope.msg to be undefined so you have to test for that. You should I think get a second trigger immediately after that will contain the initial msg. Then every time, on the server, you receive a msg into the node, it is passed to all connected clients via websockets and the $watch is triggered again.

$watch is normally a bit of an anti-method I think in Angular and is a bit of a sledge-hammer approach but it works for the kind of things we are doing here & I think that any other approach would need amendments to Dashboard to give us hooks that we could use to insert other code to the Angular data model.

Julian Knight

unread,
Sep 29, 2016, 10:04:36 AM9/29/16
to Node-RED
In this part:


            for (var i = 0; i < dataPoints.length; i++) {
                myChart
.data.datasets[i].data = dataPoints[i];
           
}

Do you really need to rebuild the data arrays each time? If a new msg passes a new value, can't you simply push() the new value onto the existing array?

Colin Law

unread,
Sep 29, 2016, 10:14:05 AM9/29/16
to node...@googlegroups.com
Do you mean that my msg.payload should just provide new samples? I
tried to work out how to do that, but I could not see how to load the
chart with its initial set on opening the tab in the browser. There
is a separate instance of a UI node for each browser session is there
not? Or am I missing something?

Colin
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/4a95a888-9d5e-47ab-9a81-2fd408d87551%40googlegroups.com.

Julian Knight

unread,
Sep 29, 2016, 11:22:22 AM9/29/16
to Node-RED
Of course. Sorry, I'm probably taking you round in circles.

So you want the initial display for any new browser session to include some historic data?

A bit hacky but maybe you could check the length of chartDef.data.datasets[0].data. If it is empty, you could send a msg back to NR in a specific format that you could then process in NR with a loop back to the template node's input via a function node that populates the initial history. You would probably have to block any normal incoming msg's too while doing the initial population.

Colin Law

unread,
Sep 29, 2016, 11:49:46 AM9/29/16
to node...@googlegroups.com
On 29 September 2016 at 16:22, Julian Knight <j.kni...@gmail.com> wrote:
> Of course. Sorry, I'm probably taking you round in circles.
>
> So you want the initial display for any new browser session to include some
> historic data?

Yes

>
> A bit hacky but maybe you could check the length of
> chartDef.data.datasets[0].data. If it is empty, you could send a msg back to
> NR in a specific format that you could then process in NR with a loop back
> to the template node's input via a function node that populates the initial
> history. You would probably have to block any normal incoming msg's too
> while doing the initial population.

OK, I will think about that, I can't say that I like it much though.
Is it worth it though I wonder. Are you principally thinking of the
amount of data sent to the browser for each sample, or the overheads
in the browser of replacing the data versus shifting and adding on the
end. I am not sure that there would be much difference in chart.js.
Also I would need the extra code that determines when to remove a
sample off the front of the chart. As I have it the template node does
not need to know the max length of the time axis.

Colin
> https://groups.google.com/d/msgid/node-red/600bbd62-4bc1-4249-a1e2-62a8df1b3695%40googlegroups.com.

Julian Knight

unread,
Sep 29, 2016, 4:41:11 PM9/29/16
to Node-RED

You are probably right. Make an interesting experiment for larger/longer data sets though. Maybe I'll revisit it at some point.

Colin Law

unread,
Sep 29, 2016, 5:02:51 PM9/29/16
to node...@googlegroups.com
When I am happy with what I have got I will try some stress tests to
see how it performs and will report back.

Thanks for the help

Colin
> https://groups.google.com/d/msgid/node-red/2e5ff429-815b-4e5c-8a9f-84ec0e62e27e%40googlegroups.com.

Larry Roy

unread,
Sep 29, 2016, 9:29:38 PM9/29/16
to Node-RED
Colin,

I am basically where you are in my experiments with Chart.js

I use .push and .shift to insert new datapoints and scroll off old data at a programmable # of points.

I haven't decided how to pre-populate my chart at initial load, and for my use-cases, I might not do anything about it at all.

As far as stress-testing, I am pumping a random value into the chart every second and scrolling off datapoints after the chart contains 300 items.

When I checked browser memory usage 24 hours later, the memory footprint hadn't changed much at all.

Larry

Colin Law

unread,
Sep 30, 2016, 4:17:08 AM9/30/16
to node...@googlegroups.com
Larry

Having thought about it some more I think I will move in that
direction also (able to send individual samples to the chart), but I
do also need to be able to populate it on initial view, so I will be
working out the best way of doing that.

Colin
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to node-red+u...@googlegroups.com.
> To post to this group, send an email to node...@googlegroups.com.
> To view this discussion on the web, visit https://groups.google.com/d/msgid/node-red/73a6cc4c-4775-460c-93ae-1a5e7305117e%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages