2

first and foremost, sorry about the broken english.

I'm using ChartJS to generate charts for a while, but today I stumbled in a problem that I can't seem to find an answer. I'm using a function to add data dynamically to my chart, then update it. It's working when I need to just add another dataset, but when I try to edit an existing dataset, I can get the values (seems like correctly) to the chart dataset, however, the chart won't show the new values, even after a update().

HTML:

  <div class="row">
    <img id="loading1" class="loading" src="resources/img/loading2.gif" hidden>
    <div id="chart1-wrapper" class="chart-wrapper">
      <canvas id="chart1"></canvas>
    </div>
  </div>
  <script>
    var ctx = document.id("chart1").getContext("2d");
    var chart1 = create_chart("chart title", "label text", "bar"); //Custom code that just create an empty chart;
  </script>

Function that creates the chart:

function create_chart(title, label_prefix, type) {
  var data = {
    labels: [],
    datasets: []
  };
  var options = {
    scales: {
      yAxes: [{
        ticks: {
          beginAtZero: true
        }
      }]
    },
    legend: {
      display: true
    },
    title: {
      display: true,
      text: title
    },
    tooltips: {
      intersect: false,
      mode: 'x',
      callbacks: {
        title: function() {}
      }
    },
    maintainAspectRatio: false,
    onClick: handleClick
  };
  var chart = new Chart(ctx, {
    type: type,
    data: data,
    options: options
  });
  return chart;
}

Here is my Update script:

function updateChart1(data, sigla){
            var dtsts;
            if(data != null){
                if(chart1.data.datasets.length == 0){
                    dtsts = [];
                    jQuery.each(JSON.parse(data), function(index, val){
                        var media = (val.soma_duracao/val.count_prot)/3600;
                        var dt_item = {
                            label: getMes(val.mes),
                            data: [media],
                            backgroundColor: [(pallete[0].rgba)],
                            borderColor: [(pallete[0].rgba.replace('0.7', '1'))],
                            borderWidth: 2
                        }
                        dtsts.push(dt_item);                        
                    });
                    console.log(dtsts);
                    chart1.data.labels = ['hours'];
                    chart1.options.title.text = "foo bar";
                    chart1.options.title.display = true;
                    chart1.options.legend = true;
                    chart1.options.tooltips.callbacks.label = function(tooltipItems, data) {
                        return tooltipItems.yLabel.toFixed(2) + " hours";
                    }                           
                } else {
                    dtsts = chart1.data.datasets;                               
                    jQuery.each(JSON.parse(data), function(index, val){
                        var media = (val.soma_duracao/val.count_prot)/3600;
                        dtsts[index].data.push(media);
                        dtsts[index].backgroundColor.push(pallete[1].rgba);
                        dtsts[index].borderColor.push(pallete[1].rgba.replace('0.7', '1'));
                    });
                    console.log(dtsts);
                }
                chart1.data.datasets = dtsts;                   
            }
            chart1.update();
        }

Running the script the first time I get this:

data:Array(1)
0:83.83071111111111
length:1
pop:ƒ ()
push:ƒ ()
shift:ƒ ()
splice:ƒ ()
unshift:ƒ ()
_chartjs: {listeners: Array(1)}
__proto__: Array(0)

Running it again updates my data:

data:Array(2)
0:83.83071111111111
1:13.746988158477201
length:2
pop:ƒ ()
push:ƒ ()
shift:ƒ ()
splice:ƒ ()
unshift:ƒ ()
_chartjs:{listeners: Array(1)}
__proto__:Array(0)

This is the correct value for my data, however, the chart simply won't update to show the values.

I'm not sure if it is some stupid mistake, but I'm just lost at this point. If anyone can shed a light, would be great. Thanks!

(PS: if you guys need any more info, please let me know)

Edit Just to clarify, I need both datas in the same dataset after the update, 2 bars of data.

urukh
  • 360
  • 1
  • 3
  • 17

3 Answers3

3

Just like Edwin Chua mentioned in one of his comments, the problem was that I wasn't including a new label for each dataset, for this reason my chart was updated, but didn't have a 'place' to show the information.

Final code for the function, if anyone needs it for future reference:

function updateChart1(data, sigla){
            if(data != null){               
                var medias = [];
                var meses = [];
                jQuery.each(JSON.parse(data), function(index, val){         
                    medias.push((val.soma_duracao/val.count_prot)/3600);                        
                    if(jQuery.inArray(val.mes, meses) == -1){
                        meses.push(getMes(val.mes));
                    }
                });
                var dtst = {
                    label: sigla,
                    data: medias,
                    backgroundColor: (pallete[0].rgba),
                    borderColor: (pallete[0].rgba.replace('0.7', '1')),
                    borderWidth: 2
                }                   
                chart1.data.labels = meses;
                chart1.data.datasets.push(dtst);
                if(chart1.data.datasets.length == 1){
                    chart1.options.title.text = "foo bar";
                    chart1.options.title.display = true;
                    chart1.options.legend = true;
                    chart1.options.tooltips.callbacks.label = function(tooltipItems, data) {
                        return tooltipItems.yLabel.toFixed(2) + " hours";
                    }
                }   
            }
            chart1.update();
        }
urukh
  • 360
  • 1
  • 3
  • 17
3

I need the second update of the chart after printing (I use another background colors in black and white print). Ordinary .update method didn't help update chart. I used setTimeout with 0.

 setTimeout(function(){
   myChart.data.datasets[0].backgroundColor = ['#f2d13a', '#c9deeb', '#cacaca'];
   myChart.update();
 }, 0);
  • 1
    Ooof. Same thing here. I call `chart.update()` and nothing happens until I hover over the canvas to cause another rerender i guess. But if i do `window.setTimeout(() => {chart.update();}, 0)` it updates the chartjs canvas...whhyyyyyy – Coty Embry Aug 02 '19 at 20:08
2

@urukh Turns out I made a mistake in my answer. You do need to add borderColor and backgroundColor for bar charts. Did a small working example of adding data to a chart (see index.html far below).

The necessary script for adding data into a chart (setInterval is a timer. Ignore in favour of your code logic):

<script>
var i = 0;
setInterval(function()
{
    console.log(myChart.data); // use this to examine the chart object for yourself

    //directly pushing into the arrays
    myChart.data.datasets[0].data.push(21); 
    myChart.data.datasets[0].backgroundColor.push('rgba(255,0,0,0.2)');
    myChart.data.datasets[0].borderColor.push('rgba(255,0,0,1)');
    myChart.data.datasets[1].data.push(21);
    myChart.data.datasets[1].backgroundColor.push('rgba(255,255,0,0.2)');
    myChart.data.datasets[1].borderColor.push('rgba(255,255,0,1)');


    myChart.data.labels.push('hi' + i++);

    //if chart dataset > 10, trim the array from the 1st element
    if (myChart.data.datasets[0].data.length > 10)
    {
        myChart.data.datasets[0].data.shift();
        myChart.data.datasets[1].data.shift();

        myChart.data.datasets[0].backgroundColor.shift();
        myChart.data.datasets[0].borderColor.shift();

        myChart.data.datasets[1].backgroundColor.shift();
        myChart.data.datasets[1].borderColor.shift();
        myChart.data.labels.shift();        
    }

    myChart.update();
}, 1000);

</script>

Run this page as index.html to see the chart in action.

<script 
src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js">
</script>

<div style="width:300px;height: 300px">
<canvas id="myChart" width="100px" height="100px"></canvas>
</div>

<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(153, 102, 255, 0.2)',
            'rgba(255, 159, 64, 0.2)'
        ],
        borderColor: [
            'rgba(255,99,132,1)',
            'rgba(54, 162, 235, 1)',
            'rgba(255, 206, 86, 1)',
            'rgba(75, 192, 192, 1)',
            'rgba(153, 102, 255, 1)',
            'rgba(255, 159, 64, 1)'
        ],
        borderWidth: 1

    },
{
        label: '# of Votes 2',
        data: [12, 5, 30, 5, 12, 23],
        backgroundColor: [
            'rgba(255, 0, 132, 0.2)',
            'rgba(54, 0, 235, 0.2)',
            'rgba(255, 0, 86, 0.2)',
            'rgba(75, 0, 192, 0.2)',
            'rgba(153, 0, 255, 0.2)',
            'rgba(255, 0, 64, 0.2)'
        ],
        borderColor: [
            'rgba(255,0,132,1)',
            'rgba(54, 0, 235, 1)',
            'rgba(255, 0, 86, 1)',
            'rgba(75, 0, 192, 1)',
            'rgba(153, 0, 255, 1)',
            'rgba(255, 0, 64, 1)'
        ],
        borderWidth: 1
    }

    ]
},
options: {
    scales: {
        yAxes: [{
            ticks: {
                beginAtZero:true
            }
        }]
    }
}
});
</script>
<script>
var i = 0;
setInterval(function()
{
    console.log(myChart.data);
    myChart.data.datasets[0].data.push(21);
    myChart.data.datasets[0].backgroundColor.push('rgba(255,0,0,0.2)');
    myChart.data.datasets[0].borderColor.push('rgba(255,0,0,1)');


    myChart.data.datasets[1].data.push(21);
    myChart.data.datasets[1].backgroundColor.push('rgba(255,255,0,0.2)');
    myChart.data.datasets[1].borderColor.push('rgba(255,255,0,1)');


    myChart.data.labels.push('hi' + i++);

    if (myChart.data.datasets[0].data.length > 10)
    {
        myChart.data.datasets[0].data.shift();
        myChart.data.datasets[1].data.shift();

        myChart.data.datasets[0].backgroundColor.shift();
        myChart.data.datasets[0].borderColor.shift();

        myChart.data.datasets[1].backgroundColor.shift();
        myChart.data.datasets[1].borderColor.shift();
        myChart.data.labels.shift();        
    }

    myChart.update();
}, 1000);

</script>
Edwin Chua
  • 658
  • 1
  • 8
  • 20
  • About the bgcolors, you're right, doesn't make sense to have an array there. Tried it, but still got the same problem, the new data is added to the dataset, but I don't get a new bar to show this new value. – urukh Oct 19 '17 at 12:36
  • @urukh Are you instantiating the chart elsewhere in your code? – Edwin Chua Oct 19 '17 at 12:51
  • have updated my answer extensively to show how to add data to a bar chart. – Edwin Chua Oct 19 '17 at 13:22
  • The interval is just so the page don't update on load, right? My function is called by a button click (too much code to post everything), so I'm not sure if this is what's wrong. – urukh Oct 19 '17 at 13:27
  • @urukh yes, substitute `setInterval` for `updateChart1()` function and you should be good. I would use `console.log(chart1)` in your code to make sure the correct chart is referenced. In any case, adding data to charts from ChartJS is a matter of directly modifying the datasets, as shown in my answer, `push()` or `shift()`, and `chart.update()` afterwards. – Edwin Chua Oct 19 '17 at 13:31
  • sorry if I'm not seeing something, but in the way that i'm doing (replacing the dataset with a new one containing the the new data); "dtsts = chart1.data.datasets; " (add data here afterwards) and "chart1.data.datasets = dtsts; " Pushing data directly, like in your example, generates the same result as this. When I print the chart1 dataset the values are there (So, this part of the code works, I guess), but the new bar doesn't appears. If I create a new chart with the same dataset, by hand, it works. – urukh Oct 19 '17 at 13:50
  • Basically, the new values are already on the chart1, but I can't get a visual representation of them – urukh Oct 19 '17 at 13:52
  • @urukh can u do a `console.log(chart1)` right at the start of your update function, and then at the end of it? Let's see whether your chart data is being updated. – Edwin Chua Oct 19 '17 at 13:59
  • Tried, the results are really updated: Datasets before first run: empty After first run: 3 datasets (from server side request), each one with only 1 data. Before second run: 3 datasets, each one with 2 data's (what?). After second run: 3 datasets, each one with 2 data's. Looks like it's updating itself before the update(), not sure how. (If you want me to paste the full result, let me know, didn't paste here because it's pretty long) – urukh Oct 19 '17 at 14:22
  • Also, tried adding a realy high value in the data. In the canvas, the y axis was updated to show the new maximum value, but still no bars to reflect this value – urukh Oct 19 '17 at 14:24
  • 1
    @urukh I THINK I GOT IT! You need to add a value to `chart.data.labels` every time you add a data into the array (eg. `chart.data.labels.push("something")`). Add *one value* everytime you add 1 value into all the datasets. Meaning this statement should always be true: `chart.data.datasets[0].data.length == chart.data.datasets[1].data.length == chart.data.labels.length`. The reason is because you need a label for each set data points on your chart! Please upvote if I helped. :) – Edwin Chua Oct 19 '17 at 15:36
  • 1
    Yeah, that was it, just came back to update and saw your new comment, haha. It's still not working the way I need, but it's a structural problem, now that I know the reason, should be easier to fix. Thank you very much for your help! – urukh Oct 19 '17 at 15:46