Search code examples
javascriptmeteoriron-router

How to get rid of Meteor template flickers


I have a Meteor template with multiple conditions, and I get a flicker of some of the conditional views when it loads initially.

I'm using iron router and I'm aware of the subscriptions, wait() and ready() options, however one of the problems is that the main conditional isInstalled depends on a meteor.call callback to set the isInstalled variable, so the wait doesn't depend on a subscription. So how do I account for this use case?

<template name="adminLayout">
    {{#if isInstalled}}
        {{#if currentUser}}
            {{> adminHeader}}
            <br /><br />
            <div class="row">  
              <div class="medium-3 columns">
              {{> adminNav}}
              </div>
              <div class="medium-9 columns">
                {{> yield}}
              </div>
            </div>
            <div class="row">
              <div class="medium-12 columns">
              {{> adminFooter}}
              </div>
            </div>
        {{else}}
            {{> login}}
        {{/if}}
    {{else}}
        {{> install}} 
    {{/if}}
</template>

Here's my Template helper, illustrating how I'm providing the value for isInstalled

Meteor.call('isInstalled', function (err, result) {
  if (err) {
    console.log(err);
  }
  Session.set('isInstalled', result);
});

Template.adminLayout.helpers({
  isInstalled: function () {
    return Session.get('isInstalled');
  }
});

And lastly the route:

Router.route('/admin', function () {
  this.layout('adminLayout');
  this.render('dashboard');
});

Solution

  • As it turns out the flickering issue is really a Meteor issue, not an Iron Router one, though iron router can provide a workaround to solve the problem using its wait() and ready() methods.

    In my particular case I didn't require a wait on a subscription but on a Meteor.call result. In order to achieve this I created an anonymous function that return an object handle with a ready method that Iron Router could understand and I could later implement in the route logic.

    Sindis guided me in the right direction though it was an incomplete solution. Below is how I accomplished it:

    Router.onBeforeAction(function (params) {
        var self = this;
        if (params.url.match(/admin/)) {
            this.wait(function(){
                Meteor.call('isInstalled', function (err, result) {
                    Session.set('installationCheck', true);
                    Session.set('isInstalled', result);
                });
                return {
                    ready: function () {
                        return Session.get('installationCheck');
                        self.next();
                    }
                }   
            });
            if (this.ready()) {
                if (Session.get('isInstalled')) {
                    this.next();
                } else if(Session.get('isInstalled') === false) {
                    console.log('go to install!');
                    this.render('install');
                }
            }
        } else {
            this.next();
        }
    });
    

    And here is a more general pattern which allows you to set routes based on asynchronous conditions

    Router.onBeforeAction(function (params) {
        var self = this;
        this.wait(function(){
            Meteor.call('someMethod', function (err, result) {
                Session.set('someMethodCalled', true);
                // do whatever with result...
                Session.set('someCondition', true); 
            });
            return {
                ready: function () {
                    return Session.get('someMethodCalled');
                    self.next();
                }
            }   
        });
        if (this.ready()) {
            if (Session.get('someCondition')) {
                this.next();
            } else if(Session.get('someCondition') === false) { // important to be explicit ===
                this.render('someSpecificRoute');
            }
        }
    });