Run code at Template start (ChartJS)


#1

First of all sorry about my stupid question _

Aaanyway I managed to implement a chart in my Meteor app using Chart.js.
The problem is i don’t know how to make it work except on a button click event.
How can i show this when my template is loaded?

    <button class="btnSS" id="btnSS">SS</button>
    <canvas id="lineChart" height="400" width="400"></canvas>

     import Chart  from 'chart
'
    'click .btnSS' : function(){
        const CHART = document.getElementById("lineChart")
        console.log(CHART)

        let lineChart = new Chart(CHART, {
            type: 'line',
            data: {
                labels: ["January", "February", "March", "April", "May", "June", "July"],
                datasets: [
                    {
                        label: "My First dataset",
                        fill: false,
                        lineTension: 0.1,
                        backgroundColor: "rgba(75,192,192,0.4)",
                        borderColor: "rgba(75,192,192,1)",
                        borderCapStyle: 'butt',
                        borderDash: [],
                        borderDashOffset: 0.0,
                        borderJoinStyle: 'miter',
                        pointBorderColor: "rgba(75,192,192,1)",
                        pointBackgroundColor: "#fff",
                        pointBorderWidth: 1,
                        pointHoverRadius: 5,
                        pointHoverBackgroundColor: "rgba(75,192,192,1)",
                        pointHoverBorderColor: "rgba(220,220,220,1)",
                        pointHoverBorderWidth: 2,
                        pointRadius: 1,
                        pointHitRadius: 10,
                        data: [65, 59, 80, 81, 56, 55, 40],
                        spanGaps: false,
                    }
                ]
            }
        })
    }

#2

Here you can find it:


#3

Great …i knew that, just forgotten about it :smiley: Thanks!


#4

I have an immediate problem doing that, so i will dare to ask a follow up question.

How can i use a variable from Template.Items.helpers in Template.Items.onRendered? Or something similar.

Example:

Template.Items.helpers({
    total: () => {
        return Items.find().count();
    }    
})

Template.Items.onRendered(function){
.......
............ data:[total]
}

#5
function getCount() {
  return Items.find().count();
}

Template.Items.helpers({
    total: () => {
        return getCount();
    }    
})

Then re-use it in your onRendered callback.


#6

I tried like u said, and if i put console.log(total) anywhere in the Template.onRendered it says total not defined. Is this normal?


#7

can you show your full example of code?


#8
import Chart  from 'chart'

function getCount() {
  return Cvs.find().count()
}

Template.Statistici.onCreated(function(){
    var self = this;
    self.autorun(function(){
        self.subscribe('cvs')
    })
})

Template.Statistici.helpers({
    totalInterviuri: () => {
        return getCount()
    },
    totalAngajati: () => {
        return Cvs.find({inMenu: true}).count()
    }    
})

Template.Statistici.onRendered(function(){
    
    const CHART = document.getElementById("lineChart")

    let lineChart = new Chart(CHART, {
        type: 'line',
        data: {
            labels: ["August", "September", "October"],
            datasets: [
                {
                    label: "Interviuri",
                    fill: false,
                    lineTension: 0.1,
                    backgroundColor: "rgba(75,192,192,0.4)",
                    borderColor: "rgba(75,192,192,1)",
                    borderCapStyle: 'butt',
                    borderDash: [],
                    borderDashOffset: 0.0,
                    borderJoinStyle: 'miter',
                    pointBorderColor: "rgba(75,192,192,1)",
                    pointBackgroundColor: "#fff",
                    pointBorderWidth: 1,
                    pointHoverRadius: 5,
                    pointHoverBackgroundColor: "rgba(75,192,192,1)",
                    pointHoverBorderColor: "rgba(220,220,220,1)",
                    pointHoverBorderWidth: 2,
                    pointRadius: 1,
                    pointHitRadius: 10,
                    data: [0, 5 ],
                    spanGaps: false,
                }
            ]
        }
    })
    //console.log(totalInterviuri)
})



#9

I don’t fully get what you want to do, where do you want to show that total? In onRendered you can just do:

data: [0, getCount() ],

Or is it something different that you need? Does it need to be reactive or something?


#10

I want to use the query in the Chart:

Template.Statistici.onRendered(function(){
        data: {
                   data: {
                   datasets: [
                          {
                               ..................
                               data: [getCount(), 5 ],

This works only if i come from another page, but if i refresh this page then the value is 0.

Must be that it’s not subscribing at the template load? Or it’s not querying the data at template load?

I tried moving things around a bit but all i get is undefined.


#11

Don’t now chart.js enough but try this first:

Template.Statistici.onRendered(() => {
  Tracker.autorun(() => {
    // YOUR CODE HERE
  });
});

I suspect it will re-render the whole graph when data changes but it may be enough.

edit:

This is the right way to do it I guess based on their docs:

http://www.chartjs.org/docs/#advanced-usage

 // keep outside of any method so we can access it everywhere
 // It might also work if you put this in onRendered but then you cannot access it from other helpers and events so simple, the you need to attach it to the template instance which goes a bit far, first try this simple solution.
let chartInstance = new Chart(CHART, { type: 'line'}); // add all config here except data


Template.Statistici.onRendered(() => {
  // will run once
  // Here you can do some custom configuration if you want, but NOT data

  Tracker.autorun(() => {
    // will run every time the count() changes
    myLineChart.data.datasets[0].data[2] = Cvs.find().count();
    chartInstance.update(); // see docs, will update axes etc
  });
});

#12

The first example (puttin everything in Tracker.autorun) works, but it gives an exception when i add or remove db items, still the app works.

The second example although i understand how it will work, no matter how i would move the code in and out of Templates i always get error like

  • cannot read property ‘length’ of null

This is my failed attempt:

import Chart  from 'chart'

Template.Statistici.onCreated(function () {
    var self = this;
    self.autorun(function () {
        self.subscribe('cvs')
    })
})

Template.Statistici.helpers({
    totalInterviuri: () => {
        return getCount()
    },
    totalAngajati: () => {
        return Cvs.find({ inMenu: true }).count()
    }
})

function getCount() {
    return Cvs.find().count()
}



Template.Statistici.onRendered(function () {

    Tracker.autorun(() => {
        //console.log(getCount())

        lineChart.data.datasets[0].data[2] = Cvs.find().count()
        lineChart.update() 
 
    })
})

      const CHART = document.getElementById("lineChart")

       let lineChart = new Chart(CHART, {
            type: 'line',
            data: {
                labels: ["Luni", "Marti", "Miercuri", "Joi", "Vineri"],
                datasets: [
                    {
                        label: "Interviuri",
                        fill: true,
                        lineTension: 0.2,
                        backgroundColor: "rgba(75,192,192,0.4)",
                        borderColor: "rgba(75,192,192,1)",
                        borderCapStyle: 'butt',
                        borderDash: [],
                        borderDashOffset: 0.0,
                        borderJoinStyle: 'miter',
                        pointBorderColor: "rgba(75,192,192,1)",
                        pointBackgroundColor: "#fff",
                        pointBorderWidth: 1,
                        pointHoverRadius: 5,
                        pointHoverBackgroundColor: "rgba(75,192,192,1)",
                        pointHoverBorderColor: "rgba(220,220,220,1)",
                        pointHoverBorderWidth: 2,
                        pointRadius: 1,
                        pointHitRadius: 10,
                        //data: [4, 6, 1, 3, 5],
                        spanGaps: false,
                    },
                ]
            },
            options: {
                //showLines: false,
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero: true,
                            //reverse: true
                        }
                    }]
                }
            }
        })
       


#13

If I can add my 2 cents …

I suspect that your error may be because your chart definition has its data property commented out (I use Highcharts, not ChartJS, so this is pure speculation).

const and let do not benefit from hoisting (unlike var), so you should ensure that your chart definition is placed before where it’s used in your autorun.

Your subscription does not need to be in an autorun as it has no reactive variables (it’s harmless, just unnecessary).

I haven’t research your lineChart.data.datasets[0].data[2] = Cvs.find().count(); as it looks very ChartJS specific, but may be worth confirming.

Your getCount() function needs to run in a reactive context to allow for data arriving after it is first run. Your uses are within a helper and an autorun (commented out), so that looks okay.

This leads me to:

import Chart from 'chart';


Template.Statistici.onCreated(function () {
  this.subscribe('cvs');
});

Template.Statistici.helpers({
  totalInterviuri: () => {
    return getCount();
  },
  totalAngajati: () => {
    return Cvs.find({ inMenu: true }).count();
  },
});

function getCount() {
  return Cvs.find().count();
}

Template.Statistici.onRendered(function () {
  const CHART = document.getElementById('lineChart');

  const lineChart = new Chart(CHART, {
    type: 'line',
    data: {
      labels: ['Luni', 'Marti', 'Miercuri', 'Joi', 'Vineri'],
      datasets: [
        {
          label: 'Interviuri',
          fill: true,
          lineTension: 0.2,
          backgroundColor: 'rgba(75,192,192,0.4)',
          borderColor: 'rgba(75,192,192,1)',
          borderCapStyle: 'butt',
          borderDash: [],
          borderDashOffset: 0.0,
          borderJoinStyle: 'miter',
          pointBorderColor: 'rgba(75,192,192,1)',
          pointBackgroundColor: '#fff',
          pointBorderWidth: 1,
          pointHoverRadius: 5,
          pointHoverBackgroundColor: 'rgba(75,192,192,1)',
          pointHoverBorderColor: 'rgba(220,220,220,1)',
          pointHoverBorderWidth: 2,
          pointRadius: 1,
          pointHitRadius: 10,
          data: [],
          spanGaps: false,
        },
      ],
    },
    options: {
      // showLines: false,
      scales: {
        yAxes: [{
          ticks: {
            beginAtZero: true,
            //reverse: true
          },
        }],
      },
    },
  });

  Tracker.autorun(() => {
    // console.log(getCount())
    lineChart.data.datasets[0].data[2] = Cvs.find().count();
    lineChart.update();
  });
});

Edit to paste actual final code I had :slight_smile:


#14

Looks like that’s not quite true, but there is a thing called a temporal dead zone if you declare let and const after they are used: http://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6. In any event, I prefer to declare before use (a side-effect of coming from a strongly typed language background).


#15

Two sidenotes to the code-samples above:

  • never use html-ids to find dom-elements in your template. Use classes or so. Meteor provides a template-scoped instance of jquery for this: this.$(".my-chart"), or this.find(".my-chart") if you need the dom-element (not the jquery-wrapper). see also http://blazejs.org/api/templates.html#Blaze-TemplateInstance-find
  • do not use arrow-functions for template helpers. In your sample it works, but you can’t access the data-context this way:
Template.Statistici.helpers({
    myhelper: () => {
        return this.somevalue; // undefined, because this is window object
    },
    myhelper() {
      return this.somevalue; // good, this is the data-context of the template
    }    
})

#16

Good catch on those - I totally missed them! :slight_smile:


#17

I can use the values directly in the data[…], but for sure i will need to mess with them for interactivity, and i could use the lineChart.update(), which doesn’t work. And until i create a non-meteor project i can’t tell why.

I am getting the correct value in the console, but the graph is showing up blank.

Template.Statistici.onRendered(function () {

    const CHART = document.getElementById("lineChart")

    const lineChart = new Chart(CHART, {
        type: 'line',
        data: {
            labels: ["One"],
            datasets: [
                {
                     ....
                    data: [],
                },
             ......
    Tracker.autorun(() => {
        //console.log(getCount())
        lineChart.data.datasets[0].data[0] = [Cvs.find().count()]
        lineChart.update() 

        //console.log(lineChart.data.datasets[1].data[0])
        console.log(lineChart.data.datasets[0].data[0])
    })

#18

I have decided to take a look at HighCharts instead for now.


#19

could you post an example repo of chart.js on github? Just interested what’s the issue.


#20

Besides the problem with .update(), it also didn’t do the animation when the browser window was maximized.

I lost like an hour trying to make a gif with the issue and post it here and on chartjs github. That didn’t work out aswell so i went rage mode and trashed it all away :smiley: