optilude
optilude

Reputation: 3548

Correctly stopping an autorun() function when template is destroyed

I have a template that contains a chart, rendered using MorrisJS. The chart should update when the currentData session variable is changed, so I have made it a reactive data source with:

Template.chart.rendered = function() {

    var template = this;

    Deps.autorun(function(c) {

        // Stop if the template is removed from the dom
        // Q: Is this really right?
        if(template.__component__.dom.parentNode() === null) {
            c.stop();
            return;
        }

        var results = Session.get('currentData');

        // ... render a chart with `results` as the data

        Morris.Bar({element: template.$(".chart-container"), data: results, ...});

    });

};

Notice how I do a fairly horrid check for when to stop the autorun above. This was necessary because without this, when I navigate away from the page using the template (I'm using iron-router) to another page and back, I get warnings in the log like "Can't select in removed DomRange". I'm pretty sure this is happening because the template instance is removed, but the autorun is still running.

I feel like I'm doing something wrong here, though. Is there (a) a better place to put the autorun so that it doesn't have this problem or (b) a better way to stop the computation when the template instance is removed from the DOM?

I tried to find a way to do it with created and destroyed handlers, but I couldn't figure out how.

Upvotes: 4

Views: 1345

Answers (2)

dana2208
dana2208

Reputation: 2031

With the new autorun

Template.chart.onRendered(function(){
  this.autorun(function(computation){
    ...
    computation.stop();
  });
});

but with this autorun, when chart template is removed from the DOM it is removed automatically. This is in Meteor documentation here:

The Computation is automatically stopped when the template is destroyed.

Upvotes: 1

saimeunt
saimeunt

Reputation: 22696

Tracker.autorun returns a handle that you can store as a template instance property, then call its stop method in the onDestroyed lifecycle event.

Template.chart.onRendered(function(){
  this.computation = Tracker.autorun(function(){...});
});

Template.chart.onDestroyed(function(){
  this.computation.stop();
});

EDIT 29-09-2014

In newer versions of Meteor (0.9 onward), there is a new autorun function available on template instances which provide simpler code to achieve the same result : no need to store and stop the computation manually, this is taken care of by the framework.

Template.chart.onRendered(function(){
  this.autorun(function(){...});
});

Upvotes: 6

Related Questions