Graph is not rendering reactively


#1

Okay, so I’m using ChartJS with my meteor project and I have added graphs that accept data. When I click a button, a new item is added to the collection, making its length 1 and the graph should render on the screen with the data that is sent in, but instead, it says “Cannot read property ‘getContext’ of null” and doesn’t render. If I press the button again or refresh the page, the graph shows up.

Here is the code that I’m working with:

Template.dashboard.rendered = function(){

  this.autorun(function(){
    if(collection.find({}).count()===1){
       buildGraph(1,2,3);
     
    }
  });

});

The buildGraph function is as such:

buildGraph = function buildgraph(x,y,z) {

    // Data and options for charts
    var lineData = {
        labels: ["Monday", "Tuesday", "Wednesday],
        datasets: [
            {
                label: "Example dataset",
                fillColor: "rgba(57,185,253,0.5)",
                strokeColor: "rgba(220,220,220,1)",
                pointColor: "rgba(220,220,220,1)",
                pointStrokeColor: "#39B9FD",
                pointHighlightFill: "#39B9FD",
                pointHighlightStroke: "rgba(220,220,220,1)",
                data: [x,y,z]
            }
        ]
    };

    var lineOptions = {
        scaleShowGridLines: true,
        scaleGridLineColor: "rgba(0,0,0,.05)",
        scaleGridLineWidth: 1,
        bezierCurve: true,
        bezierCurveTension: 0.4,
        pointDot: true,
        pointDotRadius: 4,
        pointDotStrokeWidth: 1,
        pointHitDetectionRadius: 20,
        datasetStroke: true,
        datasetStrokeWidth: 2,
        datasetFill: true,
        responsive: true,
    };


    var ctx = document.getElementById("lineChart").getContext("2d");
    var myNewChart = new Chart(ctx).Line(lineData, lineOptions);

The graph template is:

<template name="graphChart">

  <div class="wrapper wrapper-content animated fadeInRight">


    <div>
      <canvas id="lineChart" height="140"></canvas>
    </div>

  </div>

</template>

and then in my main template, I have added {{>graphChart}} where I want the graph to appear. It only shows up after refreshing the page.

What could be the problem with this?


#2

Are you waiting for your subscription to be ready?


#4

Just before I draw the graph, I used console.dir to output the data in the collection and it gave me the data that was needed, yet the graph still gave me an error. But to answer your question, I am not waiting for the subscription to be ready.

Isn’t it basically waiting for the subscription to be ready if I’m waiting for the collection be of length 1 in my tracker.autorun?


#5

The error indicates that the code cannot find the canvas element when it runs document.getElementById("lineChart").

My guess is that when you run the code new Chart(ctx).Line(lineData, lineOptions), that it updates the DOM, and then when your code reactively updates and runs your buildGraph function a second time, the canvas with id='lineChart' isn’t there anymore, and thus your code breaks. When you refresh the page, the data exists in the DB and the DOM is initialized back to its original form with the canvas element intact, so it can render the chart with data just fine (but once it does the DOM will become dirty again, and the same problem will occur if you try to update the chart)

A quick look at the docs shows that you can save the chart instance myNewChart, update the data, and then run something like myNewChart.update() to update the chart without having to reinitialize it against the DOM.


#6

Hey wallside,

Thanks for your insight. Could you explain a bit more by what you mean by save the chart instance? Also, where would myNewChart.update() go?


#7

In your rendered function, outside of autorun (because you only want this to happen once), you create the chart as you were before, ultimately running the following code once:

var ctx = document.getElementById("lineChart").getContext("2d");
var mychart = new Chart(ctx).Line(lineData, lineOptions);

Then later inside of the autorun, you use the built-in chartjs methods available on your chart instance mychart to update your chart. When you run the code new Chart(ctx).Line(lineData, lineOptions), it returns a chart instance that has methods on it to manipulate your chart. Look at the documentation here http://www.chartjs.org/docs/#advanced-usage


#8

I tried that and sadly, I kept getting the same problem. The thing is, I have a button I have set up to add an item to the collection, so the count goes to 1. The first time I press it, I get the error that the context is null and then if I press it again, the graph renders, without me having to refresh the page.


#9

I actually just figured out the solution. So, what I did is, I surrounded the lineData, lineOption and

var ctx = document.getElementById("lineChart").getContext("2d");
      var myNewChart = new Chart(ctx).Line(lineData, lineOptions);

in Tracker.autorun and fetched the collection items within the autorun and then passed the sub-items from the collection into the graph. This cleared up everything and now, the graph renders automatically.