Handlebars provides the power necessary to let you build semantic templates effectively with no frustration.
Mustache templates are compatible with Handlebars, so you can take a Mustache template, import it into Handlebars, and start taking advantage of the extra Handlebars features.
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{body}} </div> </div>
{{
, some contents,
followed by a }}
<script>
tag.
<script id="entry-template" type="text/x-handlebars-template"> template content </script>
Handlebars.compile
var source = $("#entry-template").html(); var template = Handlebars.compile(source);
var context = {title: "My New Post", body: "This is my first post!"} var html = template(context);
<div class="entry"> <h1>My New Post</h1> <div class="body"> This is my first post! </div> </div>
{{expression}}
.
If you don't want Handlebars to escape a value, use the "triple-stash".
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{{body}}} </div> </div>
{ title: "All about <p> Tags", body: "<p>This is a post about <p> tags</p>" }
<div class="entry"> <h1>All About <p> Tags</h1> <div class="body"> <p>This is a post about <p> tags</p> </div> </div>
Handlebars.SafeString
.
If you write a helper that generates its own HTML, you will usually
want to return a new Handlebars.SafeString(result)
.
In such a circumstance, you will want to manually escape parameters.
Handlebars.registerHelper('link', function(text, url) { text = Handlebars.Utils.escapeExpression(text); url = Handlebars.Utils.escapeExpression(url); var result = '<a class="' + url + '">' + text + '</a>'; return new Handlebars.SafeString(result); });
{{#list people}}{{firstName}} {{lastName}}{{/list}}
{ people: [ {firstName: "Yehuda", lastName: "Katz"}, {firstName: "Carl", lastName: "Lerche"}, {firstName: "Alan", lastName: "Johnson"} ] }
list
to generate our
HTML list. The helper receives the people
as its
first parameter, and an options hash as its second parameter.
The options hash contains a property named fn
,
which you can invoke with a context just as you would invoke a
normal Handlebars template.
Handlebars.registerHelper('list', function(items, options) { var out = "<ul>"; for(var i=0, l=items.length; i<l; i++) { out = out + "<li>" + options.fn(items[i]) + "</li>"; } return out + "</ul>"; });
<ul> <li>Yehuda Katz</li> <li>Carl Lerche</li> <li>Alan Johnson</li> </ul>
else
section (used, for instance, by the built-in if
helper).
options.fn(context)
, Handlebars does not escape
the results of a block helper. If it did, inner content would
be double-escaped!
with
Block Helper
var source = "<p>{{lastName}}, {{firstName}}</p>"; var template = Handlebars.compile(source); template({firstName: "Alan", lastName: "Johnson"});
<p>Johnson, Alan</p>
with
block helper.
<div class="entry"> <h1>{{title}}</h1> {{#with author}} <h2>By {{firstName}} {{lastName}}</h2> {{/with}} </div>
{ title: "My first post!", author: { firstName: "Charles", lastName: "Jolley" } }
<div class="entry"> <h1>My first post!</h1> <h2>By Charles Jolley</h2> </div>
each
block helper
each
helper. Inside the block, you can use this
to reference
the element being iterated over.
<ul class="people_list"> {{#each people}} <li>{{this}}</li> {{/each}} </ul>
{ people: [ "Yehuda Katz", "Alan Johnson", "Charles Jolley" ] }
<ul class="people_list"> <li>Yehuda Katz</li> <li>Alan Johnson</li> <li>Charles Jolley</li> </ul>
this
expression in any context to reference
the current context.
if
block helper
if
helper to conditionally render a block. If
its argument returns false
, undefined
, null
,
""
or []
(a "falsy" value), Handlebars will not
render the block.
<div class="entry"> {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{/if}} </div>
{}
) context, will result in:
<div class="entry"> </div>
{{else}}
is called an "else section".
<div class="entry"> {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{else}} <h1>Unknown Author</h1> {{/if}} </div>
{{! }}
<div class="entry"> {{! only output this author names if an author exists }} {{#if author}} <h1>{{firstName}} {{lastName}}</h1> {{/if}} </div>
<div class="entry"> {{! This comment will not be in the output }} <!-- This comment will be in the output --> </div>
unless
block helper
unless
helper as the inverse of the
if
helper. Its block will be rendered if the expression
returns a falsy value.
<div class="entry"> {{#unless license}} <h3 class="warning">WARNING: This entry does not have a license!</h3> {{/unless}} </div>
license
under the current context returns a
falsy value, Handlebars will render the warning. Otherwise, it will render
nothing.
<p>{{name}}</p>
<div class="entry"> <h1>{{title}}</h1> <h2>By {{author.name}}</h2> <div class="body"> {{body}} </div> </div>
var context = { title: "My First Blog Post!", author: { id: 47, name: "Yehuda Katz" }, body: "My first post. Wheeeee!" };
../
segments,
which evaluate their paths against a parent context.
<h1>Comments</h1> <div id="comments"> {{#each comments}} <h2><a href="/posts/{{../permalink}}#{{id}}">{{title}}</a></h2> <div>{{body}}</div> {{/each}} </div>
../
path segment references the parent template
scope, not one level up in the context. This is because block
helpers can invoke a block with any context, so the notion of
"one level up" isn't particularly meaningful except as a reference
to the parent template scope.
Handlebars.registerHelper
method.
<div class="post"> <h1>By {{fullName author}}</h1> <div class="body">{{body}}</div> <h1>Comments</h1> {{#each comments}} <h2>By {{fullName author}}</h2> <div class="body">{{body}}</div> {{/each}} </div>
var context = { author: {firstName: "Alan", lastName: "Johnson"}, body: "I Love Handlebars", comments: [{ author: {firstName: "Yehuda", lastName: "Katz"}, body: "Me too!" }] }; Handlebars.registerHelper('fullName', function(person) { return person.firstName + " " + person.lastName; });
<div class="post"> <h1>By Alan Johnson</h1> <div class="body">I Love Handlebars</div> <h1>Comments</h1> <h2>By Yehuda Katz</h2> <div class="body">Me Too!</div> </div>
this
context
of the function.
<ul> {{#each items}} <li>{{agree_button}}</li> {{/each}} </ul>
var context = { items: [ {name: "Handlebars", emotion: "love"}, {name: "Mustache", emotion: "enjoy"}, {name: "Ember", emotion: "want to learn"} ] }; Handlebars.registerHelper('agree_button', function() { return new Handlebars.SafeString( "<button>I agree. I " + this.emotion + " " + this.name + "</button>" ); });
<ul> <li><button>I agree. I love Handlebars</button></li> <li><button>I agree. I enjoy Mustache</button>