Search code examples
meteorchart.jsmeteor-blaze

findOne returns undefined in onRendered


I'm trying to draw a chart using chart.js. I'm able to get the chart to appear using onRendered when I use static numbers however when I try to create a variable by grabbing data from a collection the variable return undefined. I assume this is a load order problem.

Template.communication.onRendered(function() {
  // Get the context of the canvas element we want to select
  var ctx  = document.getElementById("myChart").getContext("2d");

  // get user data for charts
  var id = FlowRouter.getParam('id');
  var user = ProfessionalOverview.findOne({"candidateUserId": id});
  var communicationSkills = user && user.communicationSkills;

  // Set the data
  var data = {
    labels: ["Communication skills"],
    datasets: [
      {
        data: [communicationSkills, 2],
        backgroundColor: [
          "#FF6384",
          "#F2F2F2",
        ]
      }]
    };

    // And for a doughnut chart
    var myDoughnutChart = new Chart(ctx, {
      type: 'doughnut',
      data: data,
      options: {
        cutoutPercentage: 50,
        tooltips: false,
        legend: {
          labels:{
            display: true,
            boxWidth: 0
          },
          onClick: (e) => e.stopPropagation(),
        },
        animation:{
          animateRotate: true,
          render: false,
        },
      }
    });

  });

Solution

  • you have a race condition, i'd call it. you don't show where you have your subscribe, i assume it's in the onCreated(). if so, yes, there is no guarantee that the data will be ready when you do your find.

    the typical solution is to wrap your find in an autorun, like this:

    this.autorun(function() {
        var user = ProfessionalOverview.findOne({"candidateUserId": id});
    
        if (user) {
            // rest of the code that uses this data
        }
    });
    

    You can also use collection isReady Callback for same

    this.autorun(function() {   
        if (ProfessionalOverview.isReady()) {
            var user = ProfessionalOverview.findOne({"candidateUserId": id});
            // rest of the code that uses this data
        }
    });
    

    by protecting it with the "if", you won't bother doing any work until the data shows up.