Using Chart.js to show charts

Hi
I worked on OpenHab with Chart.js before.
So I thought to use chart.js on nodeRed dashboard as well.
I found some projects doing so.
https://flows.nodered.org/flow/c3dc75c47323a2754f5285225bce64b5
and
https://flows.nodered.org/flow/720044a3c587a310813a9326ed3cb08a
Basicaly they worked. I see the chart in dashboardUI

But for editing the code for my needs, I would like to have this JS code not in a function Node in NodeRed, but in a seperate file.
I configured httpStatic: and placed the js file there.
In a functionnode I placed

<script src="/test/js/Chart.bundle.min.js"></script>
<canvas id="myChartSimple1" width="300" height="300"></canvas>
<script src="/test/js/test.js"></script>

When I explore the Network with the browser, both files are found and loaded, but
nothing happens.

The Node in dashboardUI leaves blank.

where is my fault, what can I change?
Thank you!

Putting html in a function node will not create a web page.

Perhaps you are using the wrong terms? Did you mean a ui-template node?

Of course. Sorry.
ui-template node !!!

litter your /test/js/test.js with console.log("this is the value of x", x) and console.log("inside function DrawMyChart'') etc

deploy

refresh the browser

do you see these log entries?

I did edit the test.js. Every function got an console.log

But there are no console logs


You can see, the file itself is called and deployed?
but no console logs.

Just because a js file loads doesn't mean the code is executed. You would need to share the code for me to understand why.

It's the code from Add Chart.js charts to node-red-dashboard flows (flow) - Node-RED

The first "alert" and console log are working
alert("Beginn")
console.log("inside Beginn")

alert("Beginn")  
console.log("inside Beginn")
(function() {
	alert("vardef")  
 	console.log("inside function vardef")
   var chartID = "myChartSimple1";           // set this to the id you have specified in the canvas tag above
    // setup the chart definition as defined in the chart.js documentation, in addition setting up the topic
    // for each channel
    var chartDef = {
        type: 'line',
        data: {
            datasets: [{
                topic: "Sin",    // used here not by chart.js
                label: "Sin",
                yAxisID: "1",
                fill: false,
                lineTension: 0,
                borderColor: "#0000ff",
                pointRadius: 5,
                pointHoverRadius: 5,
                pointBorderColor: "#0000ff",
                pointBackgroundColor: "#0000ff",
                backgroundColor:  "#0000ff",
                borderWidth: 1,
                data: []  // data is written here later
            }, {
                topic: "Triangle",    // used here not by chart.js
                label: "Triangle",
                yAxisID: "2",
                fill: false,
                lineTension: 0,
                borderColor: "#ff0000",
                pointRadius: 5,
                pointHoverRadius: 5,
                pointBorderColor: "#ff0000",
                pointBackgroundColor: "#ff0000",
                backgroundColor:  "#ff0000",
                borderWidth: 1,
                data: []  // data is written here later
            }]
        },
        options: {
            scales: {
                xAxes: [{
                    type: 'linear',
                    position: 'bottom'
                    }
                ],
                yAxes: [{
                    id: "1",
                    ticks: {
                        min: -1,
                        max: 1,
                        stepSize: 0.2
                    }
                }, {
                    id: "2",
                    ticks: {
                        min: -10,
                        max: 10,
                        stepSize: 2
                    }
                }]
            },
            animation: {
                duration: 0
            }
        }
    }
        
/***** You shouldn't normally need to change anything below here *****/    
    var myChart = null;
    var loaded = false;     // indicates whether we have already had a load action
    var chartTimeSpan;
    var chartMaxPoints;

    function doChart(msg, scope) {
		alert("doChart")  
		console.log("inside function doChart")
        if (!myChart) {
            // chart does not exist so load the data and create it
            var ctx = document.getElementById(chartID);
            myChart = new Chart(ctx, chartDef);     
        }
        // chart already exists, update it
        var datasets = myChart.data.datasets;
        // is this a load or preload action?
        if (msg.action === "load" || msg.action === "preload") {
            // yes, do not allow preload if we have already had a load
            // so do it if this is a load or we have not previously had a load
            if (msg.action === "load" || !loaded) {
                // pick up chartTimeSpan and chartMaxPoints if they have been provided
                if (typeof msg.chartTimeSpan != 'undefined') {
                    chartTimeSpan = msg.chartTimeSpan;
                }
                if (typeof msg.chartMaxPoints != 'undefined') {
                    chartMaxPoints = msg.chartMaxPoints;
                }
                    
                // replace existing data for matching topics
                for (var j = 0; j < msg.payload.length; j++) {
                    var topic = msg.payload[j].topic;
                    // find it in the chart
                    for (var i = 0; i < datasets.length; i++) {
                        if (datasets[i].topic == topic) {
                            // if stripping old samples by time is required then ensure the x value is Date
                            if (chartTimeSpan > 0 ) {
                                var data = msg.payload[j].data;
                                for (var k = 0; k < data.length; k++) {
                                    if (typeof data[k].x === "string") {
                                        data[k].x = new Date(data[k].x);
                                    }
                                }
                            }
                            if (chartDef.type !== "bar") {
                                datasets[i].data = msg.payload[j].data;
                            } else {
                                // bar chart so x values must go to labels and y values to dataset
                                datasets[i].data = [];
                                myChart.data.labels = [];
                                var data = msg.payload[j].data;
                                for (var k = 0; k < data.length; k++) {
                                    datasets[i].data.push(data[k].y);
                                    myChart.data.labels.push(data[k].x);
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if (msg.action === "load") loaded = true;
            myChart.update();
        } else {
            // does the topic match one of the datasets?
            for (var i = 0; i < datasets.length; i++) {
                if (datasets[i].topic == msg.topic) {
                    // if stripping old samples by time is required then ensure the x value is Date
                    if (chartTimeSpan > 0 && typeof msg.payload.x === "string") {
                        msg.payload.x = new Date(msg.payload.x);
                    }
                    if (chartDef.type !== "bar") {
                        datasets[i].data.push(msg.payload);
                    } else {
                         // bar chart so x value must go to labels and y value to dataset
                        datasets[i].data.push(msg.payload.y);
                        myChart.data.labels.push(msg.payload.x);
                    }
                    myChart.update();
                    break;
                }
            }
        }
        // strip off samples older than now
        // charTimeSpan == 0 implies don't do it
        var shifted = false;
        if (chartTimeSpan > 0) {
            var now = new Date();
            var oldestTimeAllowed = now - chartTimeSpan;
            for (var i = 0; i < datasets.length; i++) {
                dataset = datasets[i];
                while(dataset.data[0] && getTime(dataset.data[0].x) < oldestTimeAllowed) {
                    dataset.data.shift();
                    shifted = true;
                }
            }
        }
        // strip samples off the front if there are now too many
        // charTimeSpan == 0 implies don't do it
        if (chartMaxPoints > 0) {
            for (var i = 0; i < datasets.length; i++) {
                dataset = datasets[i];
                while(dataset.data.length > chartMaxPoints) {
                    dataset.data.shift();
                    shifted = true;
                }
            }
        }
        if (shifted) {
            myChart.update();
        }
    };

    // gets the time of an x value, works for strings or Date types
    function getTime(x) {
		alert("getTime")  
		console.log("inside function getTime")
        if (typeof x === "string") x = new Date(x);
        return x.getTime();
    }
    
    // builds the preload message for sending back to the chart helper
    function preloadMsg() {
		alert("preloadMsg")  
		console.log("inside function preloadMsg")
        var preMsg = {action: "preload", payload: "preload"};
        // build array of topics in chart
        var topics = [];
        for (var i=0; i<chartDef.data.datasets.length; i++) {
            topics.push(chartDef.data.datasets[i].topic);
        }
        preMsg.topics = topics;
        // has the chart already been created
        if (myChart) {
            preMsg.lastXValue = 1;
        } else {
            preMsg.lastXValue = 0;
        }
        return preMsg;
    }

    (function(scope) {
        // this code gets run when the a view is opened on the node in the browser
        // send a preload message back to node red to ask it send
        // us a complete set of data. Pass down max points and time span to the helper node for it to use
        // plus an array of the topics of interest
        scope.send( preloadMsg() );
        
        scope.$watch('msg', function(msg) {
            if (msg) {
                doChart(msg, scope);
            }
        });
    })(scope);
})();

try this...

alert("Beginn")  
console.log("inside Beginn")
(function() { // <<<<<<< DELETE THIS LINE
	alert("vardef")  
 	console.log("inside function vardef")

// code snipped for brevity

    (function(scope) {
// code snipped for brevity
    })(scope);
})(); // <<<<<<< DELETE THIS LINE

Also, I'm not sure (function(scope) { will work in an include

You may need to put this part in the UI Template

<script src="/test/js/Chart.bundle.min.js"></script>
<canvas id="myChartSimple1" width="300" height="300"></canvas>
<script src="/test/js/test.js"></script>
<script>
    (function(scope) {
        // this code gets run when the a view is opened on the node in the browser
        // send a preload message back to node red to ask it send
        // us a complete set of data. Pass down max points and time span to the helper node for it to use
        // plus an array of the topics of interest
        scope.send( preloadMsg() );
        
        scope.$watch('msg', function(msg) {
            if (msg) {
                doChart(msg, scope);
            }
        });
    })(scope);
</script>

Completely untested ↑

You will need to figure out how it fits together.

Yes your'e right - it works.
But why does a IIFE ( Immediately Invokable Function Expression ) not work?
I'm not so familiar with javascript.
Thank you!!

It does work, the problem is an IIFE is called only once meaning the scope IIFE (function(scope) { can never be called because of initialisation timing.

I see. I always got the data in my chart.js on other projects by ajax-calls and php. I think I have to do it different in NodeRed. I will figure it out.
Thank you very much !!!!!

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