JavaScript Templating with Handlebars

by Jack Franklin

Something we do a lot in JavaScript is insert content into the DOM following the execution of some code, such as an Ajax request. Ajax request is made, it succeeds, and then as a consequence we need to update the page, to show the user the request was successful, or to perhaps show their data. Most obvious example, Ajax comments.

  1. User fills in form.
  2. Clicks “submit button”.
  3. POST request made to save data to database.
  4. DOM is updated with the user’s comment displayed.

One way you might go about this is like so:

//imagine at this point you've made the Ajax req, data is saved
//this code executes on success
var commentWrap = $("ul");
var newComment = "<li><span>" + data.username + "</span>" + "<div>" + data.comment "</div></li>";
commentWrap.append(newComment);

There’s a few way of doing things, but the key thing I’m getting at here is that horrible string that we create to insert into the DOM. When you’ve got more than one tag & one variable, it quickly gets ridiculous, messy & down right silly. Thankfully I’m not the only person to have this view, and multiple solutions have sprung up. Today I’m going to be taking a look at one such solution, Handlebars.

The first thing to do is create a new project, include jQuery & Handlebars. I grab jQuery from the Google CDN, and for now I just link directly to Github to get at the Handlebars source. I also create app.js, which is where I’ll put my code.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js" type="text/javascript" charset="utf-8"></script>
<script src="app.js" type="text/javascript" charset="utf-8"></script>

The way Handlebars works is that we define our HTML structure for what we’d like to insert into the DOM within script tags. However we give this tag a type of text/x-handlebars-template. Browsers wont recognise this, which means they wont try to parse it as JavaScript, which is key here. I also give every template a unique ID so I can get at it easily later.

Following on from my Ajax comment example, lets imagine we’ve got the code written to perform the Ajax request, and it’s all happened successfully. We’re now left with some data and we need to add this to our page. A simple Handlebars template might look like this:

<script id="ajax-comment" type="text/x-handlebars-template">
  <li>
    <span class="meta">{{name}} on {{date}}</span>
    <p>{{comment}}</p>
  </li>
</script>

Within the script tag you just enter regular HTML, and when you want to substitute something for a value, you enter it within double braces. So where I’ve typed {{name}}, Handlebars will expect to be given a variable called name and it will then substitute the variable value into our template.

So, lets see how we compile a Handlebars template. The first thing we want to do is grab the contents of our template, which is easily done with jQuery:

var source = $("#ajax-comment").html();

When you include the Handlebars source, you get a global object, Handlebars, which contains all the methods we need. We use the compile method to convert the HTML we have into a template Handlebars can understand:

var template = Handlebars.compile(source);

compile returns a function that we can call to take the template & produce our HTML, with variables all substituted correctly. To do this we pass an object with our data in. I’ve set up a basic one which replicates the data you might get if this was a real comment:

var data = {
  name: "Jack",
  date: "12/04/12",
  comment: "This is a really awesome tutorial. Thanks."
};      

Now we can compile this template fully into the HTML using template(data). That will return a string, which is our HTML. In my index.html I’ve created an empty ul so I can append a comment to it:

$("ul").append(template(data));

And it is as simple as that! However, Handlebars is capable of a lot more. Firstly, I want to tackle a problem which comes up fairly often, which is that your data you’re passing into Handlebars might itself be HTML. Suppose for example, the comments we process can have basic HTML in:

comment: "This is a really <strong>awesome</strong> tutorial. Thanks."

If you check your page in a browser, what you’ll see is:

Thankfully, the fix is really easy. Just wrap the variable within 3 sets of braces: {{{comment}}}. By default Handlebars escapes all values you pass through to it. Using 3 sets of braces tells it not to.

Handlebars is also capable of looping through data sets. Say for example in our comment, we let the user enter in their usernames on some social networks. So the data we have for Handlebars might look like:

var data = {
  name: "Jack",
  date: "12/04/12",
  comment: "This is a really <strong>awesome</strong> tutorial. Thanks.",
  social: [
    {
      site: "Twitter",
      name: "Jack_Franklin"
    },
    {
      site: "Github",
      name: "jackfranklin"
    }
  ]
};

And for every social site, you want to output a string of text. You could manually do this but why not use the power of Handlebar’s loops to do it for us?

{{#each social}}
  <span>{{site}} : {{name}}</span>
{{/each}}

It really is as easy as that. But what if we wanted to add a heading, but only if the user added some social networks? Handlebars also has if statements. We can check for social networks by checking if social.length > 0, or just checking that social.length evaluates to true.

{{#if social.length}}
  <h4>Social Networks</h4>
  {{#each social}}
    <span>{{site}} : {{name}}</span>
  {{/each}}
{{/if}}

It’s important to note you can do checks such as x > y. Handlebars' if will evaluate what you give it & see if it is true or false, and that’s all it can do. You can also combine the if with else:

{{#if social.length}}
    //do stuff
{{else}}
    //do other stuff
{{/if}}

Finally I want to take a look at Handlebars' partials. Say we decide that we’re going to use the same structure for the social networks in multiple places on the page. So we want this to be its own template, but we don’t want to repeat the code. Handlebars lets us define partial templates which we can then use in other templates. First, split up the social part of the template into its own:

<script id="social-partial" type="text/x-handlebars-template">
  <h4>Social Networks</h4>
  {{#each social}}
    <span>{{site}} : {{name}}</span>
  {{/each}}
</script>

Now we need to tell Handlebars about this partial, which we do through registerPartial:

Handlebars.registerPartial("social", $("#social-partial").html());

That means we can then reference the partial in our other template through . The > denotes a partial, and I refer to it as social as that was the first argument of the call I made to registerPartial. Now, our main template looks like so:

<script id="ajax-comment" type="text/x-handlebars-template">
  <li>
    <span class="meta">{{name}} on {{date}}</span>
    <p>{{{comment}}}</p>
    {{#if social.length}}
      {{> social}}
    {{/if}}
  </li>
</script>

Much tidier! I hope in this tutorial I’ve shown you why Handlebars (or another templating solution) is something really worth considering if you’re adding data to the front-end on a regular basis, and if you’re adding complex HTML structures. As always, if you’ve got a question, please leave a comment.