Nesting Handlebars helpers into templates.
14th Oct 2013
If you are building a lot of html templates using Handlebars then here is a walk through of slimming them down using Require.js, require handlebars plugin and Handlebars helper system.
A really good use case for this is building forms. Say you wanted to keep a common className and structure across all your app. You might start with a simple form like this one:
<form role="form"><fieldset><div class="form-group"><label for="name" class="control-label">Name</label><input class="form-control" type="text" id="name" /></div><div class="form-group form-group--email"><label for="email" class="control-label">Email</label><input class="form-control" type="email" id="email" /></div><input type="submit" /></fieldset></form>
The first thing we could do would be to create reusable helpers which will build the label
and input
elements.
Cheeky little helpers
It's great starting small as it allows you to be quite granular when using helpers within your templates. You can always build helpers which utilise other helpers.
Let's build a simple label
helper:
define(function(require){
// register our dependencies
var Handlebars = require('handlebars');
// create a label helper
Handlebars.registerHelper(
'input_label',
// we'll make context be the text
function(context, options){
// we'll send in the id as an attribute
id = options.hash.id;
return '<label for="'+ id +'" class="control-label">'+ context +'</label>';
}
);
});
Now after this simple helper and maybe a similar one for the input we can change the template to:
<pre><code><form role="form"><br/> <fieldset><br/> <div class="form-group"><br/> {{{input_label 'Name' id='name'}}}<br/> {{{input_text my_var id='name'}}}<br/> </div><br/> <div class="form-group form-group--email"><br/> {{{input_label 'Email' id='email'}}}<br/> {{{input_email my_date id='email'}}}<br/> </div><br/> <input type="submit" /><br/> </fieldset><br/></form><br/></code></pre>
Already it's looking better and we've saved ourselves a lot of refactoring should we need to change class names or add another attribute to the input
element across all email inputs.
Helping helpers
Now we could take this one step further by encapsulating both label and input in a helper saving us the need to repeat the id. Also it's not too pretty having html embedded in the JavaScript so lets also use the power of Require.js and the Handlebars plugin to really simply use another template inside.
define(function(require){
// register our dependencies
var Handlebars = require('handlebars');
// template helpers
var group_text = require('hbs!templates/group_text');
// create previous helpers
// ...
// create a group helper
Handlebars.registerHelper(
'group_text',
function(context, options){
// create a simple link to the value
// so we can just pass the options
// as data
options.value = context;
return group_text(options);
}
);
// create other group helpers
// ...
});
Then within our group_text.hbs
file we can not only place the surrounding markup but we can also use the helpers we've created previously.
<pre><code><div class="form-group"><br/> {{{input_label hash.label id=hash.id}}}<br/> {{{input_text value id=hash.id}}}<br/></div></code></pre>
And let's look at our resulting form template.
<pre><code><form role="form"><br/> <fieldset><br/> {{{group_text my_var label='Name' id='name'}}}<br/> {{{group_email my_date label='Email' id='email}}}<br/> <input type="submit" /><br/> </fieldset><br/></form><br/></code></pre>
Soooo much neater!! Much easier to maintain across all the forms within your app.