Search code examples
javascriptmeteord3.jsmeteor-blazemeteor-helper

Template level reactivity in Meteor


I'm working on a problem where I want to display data in a dashboard both as a chart (via perak:c3) and in a table (via aslagle:reactive-table). My issue is that the data is pulled from a collection in MongoDB, and it's format is instantly amenable to plotting via c3, but needs to be transformed into a local collection to be used by the reactive-table package, as suggested in this answer to a previous question.

When I change the dataset to be displayed I want the chart to be updated, and the table also. This requires changing the values in the local collection, however, which slows things down and so rather than the chart being smoothly redrawn, there is a freeze on the page, and then the new data is displayed.

I have created a sample project on GitHub here so the problem can be replicated easily. If you run the app and select a dataset in your browser you will see exactly what I mean

enter image description here

To see the reactive behaviour I want to preserve in the chart go to client/templates/dashboard/dashboard.html and simply comment out the table template {{> dashboardTable}}

enter image description here

and now change the dataset to see how the chart is smoothly redrawn. Essentially I am trying to ensure both templates dashboardChart and dashboardTable render independently of one another.


UPDATE

Following Michael Floyd's suggestion of using a timeout helped a bit

Meteor.setTimeout(function(){createLocalCollection(data)},200);

but although the chart gets smoothly drawn, when the table finishes being filled, the chart is drawn again. It looks like it jumps to some intermediate state that I can't understand. Here is a video showing what I mean.


Solution

  • I'm adding this as a separate answer because it's a completely different approach.

    Use the onRendered callback in d3 to invoke the local collection update.

    Where you have:

    chart = c3.generate({
      bindto: '#dataset-chart',
    

    in dashboard_chart.js, add:

    chart = c3.generate({
      onrendered: createLocalCollection(),
      bindto: '#dataset-chart',
    

    Of course you need to remove createLocalCollection(data) from your event handler.

    To avoid having to pass the data context through the onrendered handler in d3 also update your createLocalCollection function to use the reactive variable datasetID that you defined earlier to establish the current dataset:

    var createLocalCollection = function() {
        var values = My_First_Collection.find({datasetID: datasetID.get()}).fetch();
        var tempDoc = {};
        local.remove({});
        tempDoc = {};
        for (var i in values[0].value) {
          tempDoc.value = values[0].value[i];
          tempDoc.date = values[0].date[i];
          local.insert(tempDoc);
        }
    };
    

    Using this method you let D3 tell you when the chart rendering is done and then your table can start getting populated. The result is an instantaneous chart update followed by the table updating. No mucking with timeouts either.