Adding realtime data to chart.js line charts

Adding realtime data to chart.js line charts
typescript
Ethan Jackson

I am using line chart (chart.js) to display two pens of data over time. In the sample code below the chart is created and 30 seconds of historical data is immediately added to each pen. Thereafter a single point of realtime data (current time) is added to each pen via the OnTimer() function which is executed every second.

If one uses only one pen (const _PENS = 1;) the realtime data is correctly added to the historical data (right hand side of chart) as one would expect. Try it.

If one uses two pens (const _PENS = 2;) the realtime data is NOT added to the historical data but added to the left hand side of the chart. Try it.

How does one fix the chart configuration in the code below so that realtime data for multiple pens is always appended to the historical data?

<html> <body> <canvas hidden="0" id="myLineChart"></canvas> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/

Answer

There's a simple error in the original code: in the data initialization sequence the same label is added multiple times, that is once for each "pen". So, when there are two pens, there are 2 * _HISTORY labels, while the datasets have only _HISTORY values; when the new data values are added on timer, they are paired with the duplicated (supplemental) labels already set, and the new labels are ignored.

That can be corrected easily, either by guarding the labels.push with an if(i===0) that will make the push to the labels execute just for one dataset, and not for each one:

for (i = 0; i < _PENS; i++) { // --- get the time _HISTORY secs ago dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (j = 0; j < _HISTORY; j++) { // add one second dt = new Date(dt.getTime() + 1000); // add the new data point to the chart if(i === 0) { MyLineChart.data.labels.push(dt); } MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i); } MyLineChart.update(); }

jsFiddle demo.

or by reversing the two fors in the initialization code:

var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig); dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (j = 0; j <= _HISTORY; j++) { // add one second dt = new Date(dt.getTime() + 1000); MyLineChart.data.labels.push(dt); for (i = 0; i < _PENS; i++) { MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i); } MyLineChart.update(); }

jsFiddle demo.

There's also a chart.js specific approach that simplifies things somehow: to use the object data structure, and eliminate the labels; that will allow one to add the same "label" (x coordinate) for each dataset, as it was in the original code:

for (i = 0; i < _PENS; i++) { // --- get the time _HISTORY secs ago dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (j = 0; j < _HISTORY; j++) { // add one second dt = new Date(dt.getTime() + 1000); // add the new data point to the chart MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i}); } MyLineChart.update(); }

The code has to be adapted for the OnTimer procedure, snippet demo:

var MyDataset = { datasets: [ { label: 'Red pen (seconds)', borderColor: 'red', backgroundColor: 'red', }, { label: 'Orange pen (seconds+1)', borderColor: 'orange', backgroundColor: 'orange', }, ], }; // --- define my chart configuration var MyChartConfig = { type: "line", data: MyDataset, options: { plugins: { title: { display: true, text: 'My Line Chart' } }, scales: { x: { type: 'time', time: { unit: 'second', displayFormats: { second: 'YYYY-MM-DD, HH:mm:ss', // Format for the unit } }, }, y: { position: 'left', type: 'linear', display: true, title: { display: true, // Set to true to display the title text: 'Left axis', // The text content of the y-axis title }, }, }, }, }; // --- set the number of pens to use // using only 1 pen produces the desired and correct effect - realtime data is appended to the historical data on the right hand side of the chart // using 2 pens gives a whacky result - realtime data is added to the left hand side of the chart !!! const _PENS = 2; // pens const _HISTORY = 10; // seconds // --- add an OnTimer function to execute every second // this function will add realtime data to the pens function OnTimer() { // --- add a new value to each pen at the current timestamp dt = new Date(); //MyLineChart.data.labels.push(dt); for (i = 0; i < _PENS; i++) { MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i}); } MyLineChart.update(); } // --- create the chart var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig); for (i = 0; i < _PENS; i++) { // --- get the time _HISTORY secs ago dt = new Date(new Date().getTime() - (_HISTORY*1000)); for (j = 0; j < _HISTORY; j++) { // add one second dt = new Date(dt.getTime() + 1000); // add the new data point to the chart MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i}); } MyLineChart.update(); } // --- execute the OnTimer function every second const interval = setInterval(OnTimer, 1000);
<div style="min-height: 250px"> <canvas id="myLineChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/

Related Articles