Meteor Blaze Templates done right!

I often hear amazing and simple people find Meteors Blaze render engine, but in the same sentence mention how its difficult to manage large templates or create re-useable components.

In my last two+ years working with Meteor I came across the same hurdles, but also found great solutions. So I thought its time to share my best practices in a blog post.

Though this post is for advanced user, I want to give new comers a short intro into Meteors. If you’re looking for something specific you can jump right there:

Blaze

Blaze is the render engine from the Meteor guys, which takes care that parts of the template down to fine grained attributes are updated reactively. It looks bit like react:

// postList template
Template.__define__("postList", (function() {
  var view = this;
  return [
    HTML.Raw("<h1>Post List</h1>\n  "),
    HTML.UL("\n    ", Blaze.Each(function() {
      return Spacebars.call(view.lookup("posts"));
    },
    function() {
      return [ "\n      ", HTML.LI(Blaze.View(function() {
        return Spacebars.mustache(view.lookup("title"));
      })), "\n    " ];
    }), "\n  ")
  ];
}));

Spacebars

Luckily we don’t have to deal with this JavaScript spaghetti, because Blaze comes with its own templating engine called Spacebars and looks like this:

<template name="myQrCode">
  <p><div class="qrcode">{{myQrCode}}</div></p>
  <p> {{i18n 'wallet.accounts.qrCodeLabel'}} </p>
</template>

Spacebars is derived from Handlebars and works almost exactly the same, with the simple difference that its Helpers are all reactive.’

Reactive means they can re-run and update the template when its underlying reactive data changes.

How to do templates right!

Writing templates is very simple in Meteor. You wrap HTML into a <template name="myTemplate"> tag and reference it with Template.myTemplate in JavaScript to add helpers and events. So thats easy.

But when you want to build more complex templates you fast get into the problem that you want to pass a state around in your template, access this state from the outside or pass a different state in.

Most people start with using the global Session.get()/Session.set() variable. Its accessible everywhere, reactive and survives hot code reloads.

Though the problem is that you run into namespace conflicts on larger apps and your Session variable is not template instance specific, but rather changes all template instances of a specific template when used! So its basically a very bad choice for a template state.

Using template-var

Exactly because of this problem I created a package called frozeman:template-var. It works like Session but is template instance specific. The only draw back is that it doesn’t survive hot code reloads (Though i might add this in the future?).

Using TemplateVar you can solve all the problems of ordinary Blaze templates. You can simply add it by running:

$ meteor add frozeman:template-var

Passing data around

Mostly you want to do things based on user interaction. In the following example we simple hide or show a section based on a button click, as well as count up a counter based on this.

HTML:

<template name="showHide">
    <h1>You showed this already {{TemplateVar.get "counter"}} times.</h1>

    <button>
        {{#unless TemplateVar.get "show"}}
            Show
        {{else}}
            Hide
        {{/unless}}
    </button>

    {{#if TemplateVar.get "show"}}
        <p>I was just hidden</p>
    {{/if}}
</template>

JS:

Template['showHide'].onCreated(function(){

    // set the initial value
    TemplateVar.set('counter', 0);

    // This will count the counter up
    this.autorun(function() {
        if(TemplateVar.get('show')) {
            // we need to wrap this to prevent this.autorun
            // reacting on the TemplateVar.get('counter')
            // Otherwise this would create an infinite loop
            Tracker.nonreactive(function() {
                var count = TemplateVar.get('counter');
                TemplateVar.set('counter',  ++count);
            });   
        }
    });
});


Template['showHide'].events({
    'click button': function() {
        // template events are not reactive,
        // so we can use the get and set here in the same function
        TemplateVar.set('show',  !TemplateVar.get('show'));
    }
});

This is enough to have a section, which can show hide and even count this. To show that it is really template specific we can simple add two of these templates and would get a result that looks like this:

TemplateVar show/hide example

As you can see you can use the TemplateVar.get/set in templates, but also template helpers and events.

Accessing template vars from inside callbacks

In the case you want to change a TemplateVar from inside callback you can simple pass the template instance yourself as the first parameter, as it could not find the current template instance itself:

// In the created, rendered and destroyed functions
Template['myTemplate'].onCreated(function(){
    var template = this;

    MyCustomAsyncFunction(function(error, result) {
        TemplateVar.set(template, 'myResult',  result);
    });
});

// In a template helper
Template['myTemplate'].helpers({
    'myHelper': function(){
        var template = Template.instance();

        MyCustomAsyncFunction(function(error, result) {
            TemplateVar.set(template, 'myResult',  result);
        });
    }
});

// In an event
Template['myTemplate'].events({
    'click button': function(e, template) {
        MyCustomAsyncFunction(function(error, result) {
            TemplateVar.set(template, 'myResult',  result);
        });
    }
});

Reacting on data context changes

Sometimes you want to react on data context changes of data passed into a template. Doing this is not quite straight forward and clean, but works reliably.

You simple create template helper, which you put on the top of your page. This helper will re-run on data context properties that are accessed inside.

HTML:

<body>
    {{> myTemplate someProperty=changingValue}}
</body>

<template name="myTemplate">
    {{reactiveDataContext}}

    Some other HTML...
</template>

JS:

Template['myTemplate'].helpers({
    'reactiveDataContext': function(){
        if(this.someProperty === 'someValue')
            TemplateVar.set('doSomething',  'here');
    }
});

Passing data in and out of the template

Passing data in, can be done via the templates data context, but getting data out was hard to impossible in a simple way.

Using TemplateVar.getFrom() and TemplateVar.setTo() you can get or set the TemplateVars of any template from the outside by using an element inside the template as the anchor.
This is possible due to Blaze.getView('.some-selector') offered by Blaze.

This way you can simply build a component where you can set and get values from, as in the following example.

HTML:

<body>
    {{> myComponent value="hello"}}
</body>

<template name="myComponent">
    <div class="my-component">
        {{reactiveDataContext}}
        <input type="text" value="{{value}}">
    </div>
</template>

JS:

var checkValue = function(value) {
    // only allow strings not numbers
    if(!_.isFinite(value))
        TemplateVar.set('value', value);
    else
        TemplateVar.set('value', null);
};


Template['myComponent'].helpers({
    'reactiveDataContext': function() {
        // change the value if the data context changes
        // this also runs on start and sets the inital value
        checkValue(this.value);
    }
});

Template['myComponent'].events({
    'change input, input input': function(e, template) {
        checkValue(e.currentTarget.value);
    }
});

Now we can call the following to get the value of our “component” from any other template, or the console:

> TemplateVar.getFrom('.my-component', 'value');
"hello"

If we would type a number into the input field of the component and try to get the value we would get:

> TemplateVar.getFrom('.my-component', 'value');
null

We can use this helper also from inside templates, but be aware that the template you want to get data from must be present before this template is rendered:

<p>The value is: {{TemplateVar.getFrom ".my-component" "value"}}</p>

Setting value

If we wanted to overwrite the templates value, or trigger some action in our component we can simple set call TemplateVar.setTo() to change TemplateVars of a specific template instance:

> TemplateVar.setTo('.my-component', 'value', 'My New Value');

Conclusion

You see that with the right tools you can use Blaze very comfortably to build components and complex templates.

Though some things are still a bit hackie and I would love MDG to integrate these TemplateVars and accessors natively into Blaze.

  • Manu

    Thank you, wonderful plugin! Solves the Session mess 🙂