Block helpers make it possible to define custom iterators and other helpers that can invoke the passed block with a new context.
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{#noop}}{{body}}{{/noop}} </div> </div>
noop helper will receive an options hash.
This options hash contains a function (options.fn)
that behaves like a normal compiled Handlebars template.
Specifically, the function will take a context and return a
String.
Handlebars.registerHelper('noop', function(options) { return options.fn(this); });
this, so you can simply invoke the block with
this to evaluate the block in the current context.
with helper
noop helper, it
should be obvious how to implement a with helper.
Instead of invoking the block with the current context, we will
invoke it with whatever context the template passed in.
<div class="entry"> <h1>{{title}}</h1> {{#with story}} <div class="intro">{{{intro}}}</div> <div class="body">{{{body}}}</div> {{/with}} </div>
{
title: "First Post",
story: {
intro: "Before the jump",
body: "After the jump"
}
}
noop helper. Note that helpers take
parameters, and parameters are evaluated just like expressions
used directly inside {{mustache}} blocks.
Handlebars.registerHelper('with', function(context, options) { return options.fn(context); });
each helper works.
<div class="entry"> <h1>{{title}}</h1> {{#with story}} <div class="intro">{{{intro}}}</div> <div class="body">{{{body}}}</div> {{/with}} </div> <div class="comments"> {{#each comments}} <div class="comment"> <h2>{{subject}}</h2> {{{body}}} </div> {{/each}} </div>
each
once for each element in the comments Array.
Handlebars.registerHelper('each', function(context, options) { var ret = ""; for(var i=0, j=context.length; i<j; i++) { ret = ret + options.fn(context[i]); } return ret; });
<ul> wrapper, and wraps each resulting element
in an <li>.
{{#list nav}} <a href="{{url}}">{{title}}</a> {{/list}}
{
nav: [
{ url: "http://www.yehudakatz.com", title: "Katz Got Your Tongue" },
{ url: "http://www.sproutcore.com/block", title: "SproutCore Blog" },
]
}
each
helper.
Handlebars.registerHelper('list', function(context, block) { var ret = "<ul>"; for(var i=0, j=context.length; i<j; i++) { ret = ret + "<li>" + block(context[i]) + "</li>"; } return ret + "</ul>"; });
Handlebars.registerHelper('list', function(context, block) { return "<ul>" + context.map(function(item) { return "<li>" + block(item) + "</li>"; }).join("\n") + "</ul>"; });
if and
unless control structures are implemented as regular
Handlebars helpers.
{{#if isActive}} <img src="star.gif" alt="Active"> {{/if}}
Handlebars.registerHelper('if', function(conditional, options) { if(conditional) { return options.fn(this); } });
else functionality
to block helpers.
{{#if isActive}} <img src="star.gif" alt="Active"> {{else}} <img src="cry.gif" alt="Inactive"> {{/if}}
else fragment
as options.inverse. If the template provides no inverse
section, Handlebars will automatically create a noop function, so
you don't need to check for the existance of the inverse.
Handlebars.registerHelper('if', function(conditional, options) { if(conditional) { return options.fn(this); } else { return options.inverse(this); } });
list helper
from earlier, and make it possible for us to add any number of
optional attributes to the <ul> element we will
create.
{{#list nav id="nav-bar" class="top"}} <a href="{{url}}">{{title}}</a> {{/list}}
options.hash. This
makes it easier to accept a variable number of parameters, while
also accepting an optional Hash. If the template provides no hash
arguments, Handlebars will automatically pass an empty object
({}), so you don't need to check for the existance of
hash arguments.
Handlebars.registerHelper('list', function(context, options) { var attrs = SC.keys(options.hash).map(function(key) { key + '="' + options.hash[key] + '"'; }).join(" "); return "<ul " + attrs + ">" + context.map(function(item) { return "<li>" + options.fn(item) + "</li>"; }).join("\n") + "</ul>"; });
Block helpers can also inject private variables into their child templates. This can be useful to add extra information that is not in the original context data.
For example, when iterating over a list, you may provide the current index as a private variable.
{{#list array}} {{@index}}. {{title}} {{/list}}
Handlebars.registerHelper('list', function(context, options) { var out = "<ul>", data; for (var i=0; i<context.length; i++) { if (options.data) { data = Handlebars.createFrame(options.data); } out += "<li>" + options.fn(context[i], { data: data }) + "</li>"; } out += "</ul>"; return out; });
data option are
available in all descendent scopes.