R.E.M. Web Development

Using Handlebars with Backbone and RequireJS and Precompiling templates

PRE-COMPILING HANDLEBARS TEMPLATES WITH NODE

Note: I did discover something a little strange...that this process doesn't work if there is only one template file that you are compiling. When my 'templates' dir had more than one .hbs file in it, then everything worked just fine. Not sure why that is.

Even more strange: I later found that even if you only have one template to compile, you can still get things to work by changing your syntax in your view. In my example below (the backbone view), I set the 'template' property to Templates['my-view.hbs']. I found that you can also set the value to Handlebars.templates['my-view.hbs']. In this case, it will work even if you have only one template file that gets compiled.

 

1. Create your template files and put them all in one folder (I put mine in a 'templates' folder that sits next to the 'js' folder of my project. I used the .hbs extension. Note that you should NOT have <script> tags enclosing your templates.

 

2. Install node handlebars (globaly)

npm install -g handlebars

 

 

3. Cd into the directory where your templates are and compile them all into one .js file (called compiled-templates.js) like this:

handlebars --amd *.hbs -f compiled-templates.js

 

 

4. Here's a sample of how to use the compiled-templates in your view. Just include compiled-templates.js in your define (aliased as Templates) and then set this.template to Templates['template name']. This example assumes that one of your uncompiled template files was named my-view.hbs:

define(['backbone','underscore','jquery', 

"../../templates/compiled-templates"

],

function ( Backbone, _, $, Templates) {

var MyView = Backbone.View.extend({

template: Templates['my-view.hbs'],

render: function(){

this.$el.html(this.template({somevar: "HIIIIIIII"}));

return this;

}

});

return MyView;

});

 

PRECOMPILING HELPERS

Create a requirejs module and put all your helpers in it:

 

define(['handlebars'], function (Handlebars) {
Handlebars.registerHelpers('myHelper', function(){...});
Handlebars.registerHelpers('anotherHelper', function(){...});
});

 

 

 

BEFORE I FIGURED OUT HOW TO PRECOMPILE, I WOULD COMPILE TEMPLATES AT RUN TIME USING THESE STEPS

1. Make sure handlebars is specified in the bower install script (in component.json)

2. Make sure the requirejs text plugin is also installed in the bower script.

3. Make sure that a 'path' is specified to the handlebars library in main.js (it also needs to be shimmed and exported to make it amd compliant)

4. Create a templates directory in the 'app' folder

5. Inside that 'templates' dir create a sample template file (it's called sample-template.hbs):

 

<h3>{{someVar}}</h3>

<select>{{#each selectOptions}}

<option value="{{id}}" {{#if selected}}selected="true"{{/if}}>{{label}}</option>

{{/each}}

</select>

 

 

6. Create a module that compiles all templates (the file is called compiled-templates.js and it's located in the 'app' dir)

 

define([

'handlebars',

'text!../templates/sample-template.hbs',

'text!../templates/main-menu.hbs'

], function(Handlebars, SampleTemplateFile, MainMenuFile){



return {

SampleTemplate: Handlebars.compile(SampleTemplateFile),

MainMenuTemplate: Handlebars.compile(MainMenuFile)

}



});

 

 

7. Create a view that depends on the 'compiled-templates' module

 

define([

"jquery",

"underscore",

"backbone",

"handlebars",

"compiled-templates"

], function($, _, Backbone, Handlebars, JST){



var SampleView = Backbone.View.extend({



template: JST.SampleTemplate,



render: function(){



var selectOptions = [

{id:1, label:"coach"},

{id:2, label:"quarterback", selected: true},

{id:3, label:"kicker"}

];



this.$el.html(this.template({someVar:"HIIIIIII", selectOptions: selectOptions}));

return this;

}



});



return SampleView;

 

 

NOTE: If you didn't want to use the precompiled template (for some reason), you could compile it directly in the view like this (note that instead of depending on the 'precompiled-templates' module, you would have to depend on the template file directly, using the text plugin):

 

define([
"jquery",
"underscore",
"backbone",
"handlebars",
"text!../../templates/sample-template.hbs"
], function($, _, Backbone, Handlebars, PlainTextTemplate){

var SampleView = Backbone.View.extend({

template: Handlebars.compile(PlainTextTemplate),

render: function(){

var selectOptions = [
{id:1, label:"coach"},
{id:2, label:"quarterback", selected: true},
{id:3, label:"kicker"}
];

this.$el.html(this.template({someVar:"HIIIIIII", selectOptions: selectOptions}));
return this;
}

});

return SampleView;

});