FlowRouter: How to trigger jQuery-based DOM updates when a ReactiveVar has changed?


#1

Scenario:

I am using an image carousel (slick.js) that is transforming a set of DIV’s containing images into a slide-show. This image carousel is a jQuery extension that has to be initialized by calling $(’.slick-carousel’).slick(). I am also using FlowRouter for routing.

Following FlowRouter’s recommendation, I do not want to set the data context for my template inside the route, i.e. before BlazeLayout.render() is called (which works fine). Instead, I want to set it the data context for the template in the template itself.

Here’s my code for this:

Template.thingOverview.helpers({
  // Returns the Thing that should be displayed.
  activeThing: function() {
    return Template.instance().activeThing.get();
  }
});

Template.thingOverview.onCreated(function() {
  var self = this;
  self.activeThing = new ReactiveVar();
  var activeThingId = FlowRouter.getParam('thingId');
  Meteor.call('getThingFromDatabase', { _id: activeThingId }, function(error, result) {
    if (error) {
      // error handling removed for sake of simplicity
    } else {
      self.activeThing.set(result);
    }
  });
});

Template.thingOverview.onRendered(function() {
  // Initialize slick carousel.
  $('.slick-carousel').slick({});
});

At the time the template is created (onCreate), the actual number of images and their URLs are unknown. I have to retrieve them from my database. So, I am using a Meteor.call() for this, and a ReactiveVar to set a new data context for the template as soon as the callback returns, which is then returned by a template helper to the template.

In Template.onRendered(), I’ve placed the initialization call for the carousel and expected this would work.

Here’s my problem with this approach: It turns out that quite often the .slick() call is executed before the callback set in Meteor.call() has returned. So the carousel is initialized before its contents are available and thus is not displayed correctly. Obviously, the .onRendered() runs before the template has performed the DOM manipulations triggered by the ReactiveVar in the helper method.

Is there anything I can do to have the carousel initialization triggered after the template has been re-rendered completely? I thought about using a sub-template for the carousel, but I assume this will only cause the same problem on a deeper template level.

Update: I’ve now tried the nested template approach, and this worked. Still not sure if this is just “by chance” and there is a more elegant way to solve this?


#2

you can put that initialization inside autorun which will execute it only if activeThing.get() is not null.
Annd when that reactive var change it will rerun and initialize.
I would put it inside onRendered, as it needs to be run after DOM is rendered.

good luck