Advertisement
JavaScript & AJAX

An Introduction to Handlebars

by

If your site's data regularly changes, then you might want to take a look at Handlebars. Handlebars is a template processor that dynamically generates your HTML page, saving you time from performing manual updates. In this tutorial, I'll introduce you to Handlebars, and teach you how to create a basic template for your site.


Site Template

There are two primary reasons why you'd want to make a template for your site. First of all, building a template encourages you to separate the logic-based code from the actual view, helping you adhere to a View/Controller pattern. Secondly, templates keep your code clean and maintainable, which, in turn, makes the process of updating your site a breeze. You don't create a site with Handlebars. Instead, you create guidelines and structures that dictate how the site should look without focusing on a page's data. Let's cover some of the basics.


The Basics

Handlebars generates your HTML by taking a JSON structure and running it through a template. These templates are written mostly in regular HTML, and are peppered with placeholders that allow you to inject data, as needed. For example, the following template greets the user when they log in:

<h1>Welcome back, {{name}}</h1>

The {{name}} attribute is where the user's name will be injected into the page. This placeholder corresponds with a property in the data's JSON structure. This is the most basic example possible, but you will soon see that everything else basically boils down to this simple concept. Let's move on to handling arrays.

Arrays

Handlebars comes with some built-in helpers to assist you in working with more complex data. One of these helpers is the each helper. This helper iterates through an array and allows you to create dynamic HTML, per array element. For example, the following template displays an array's data that contains a list of the local concerts playing in my area:

<table>
	<tr>
		<th>Local Concerts</th>
	</tr>
	{{#each Concerts}}
		<tr>
			<td>{{this}}</td>
		</tr>
	{{/each}}
</table>

As you can see, this code is much cleaner than conventional code, such as using a loop in PHP or JavaScript to append HTML to a variable. Handlebars is not intrusive, and this is what makes Handlebars so accessible. You may also notice that we use the attribute name, this, to retrieve the current array element in the each loop.

This example is good for an array of simple values, but how do you handle more complex data? Well, you essentially do the same thing. For example, we're going to write a template for the following data:

[	
	{
		Name : "Band",
		Date : "Aug 14th, 2012",
		Albums : [
			{
				Name : "Generic Name"
			},
			{
				Name : "Something Else!!"
			}
		]
	},
	{
		Name : "Other Guys",
		Date : "Aug 22nd, 2012"
		Albums : [
			{
				Name : "Album One"
			}
		]
	}
]

We can easily display this information using the following template:

<table>
	<tr>
		<th>Band Name</th>
		<th>Date</th>
		<th>Album Name</th>
	</tr>
	{{#each Bands}}
		<tr>
			<td>{{Name}}</td>
			<td>{{Date}}</td>
			<td>{{Albums.0.Name}}</td>
		</tr>
	{{/each}}
</table>

You can store your template in a <script /> element and load it with JavaScript.

In Handlebars, you can even access nested properties, like in the example above (Albums.0.Name), and of course, you could have used another each loop to iterate over a band's albums. It's worth noting that besides the dot notation to access nested properties, you can also use "../" to access a parent's properties.

What if there aren't any bands playing? You certainly don't want an empty table, and Handlebars thankfully provides if, else and unless helpers. The if and else statements work like most programming languages: if the object you pass is false or falsey, then the else statement executes. Otherwise, the if statement executes. The unless statement is pretty interesting; it's essentially an inverted if statement. If the expression is true, the unless block will NOT run. So let's incorporate these helpers into our code:

{{#if Bands}}
	<table>
		<tr>
			<th>Band Name</th>
			<th>Date</th>
			<th>Album Name</th>
		</tr>
		{{#each Bands}}
			<tr>
				<td>{{Name}}</td>
				<td>{{Date}}</td>
				<td>{{Albums.0.Name}}</td>
			</tr>
		{{/each}}
	</table>
{{else}}
	<h3>There are no concerts coming up.</h3>
{{/if}}

Custom Helpers

Handlebars gives you the ability to create your own custom helper. Simply register your function into Handlebars, and any template you compile afterwards can access your helper. There are two kinds of helpers that you can make:

  • Function helpers are basically regular functions that, once registered, can be called anywhere in your template. Handlebars writes the function's return value into the template.
  • Block helpers are similar in nature to the if, each, etc. helpers. They allow you to change the context of what's inside.

Let me show you a quick example of each. First, I'll register a function helper with the following code:

Handlebars.registerHelper("Max", function(A, B){
	return (A > B) ? A : B;
});

The first argument passed to registerHelper() is the name of my customer helper; I'll use this name in the template. The second argument is the function associated with this helper.

Using this helper in a template is extremely simple:

{{Max 12 45}}

This template uses the Max helper, and passes the values 12 and 45 to the associated function. Handlebars function helpers support multiple parameters. You can directly insert numbers into the template itself, or you can use attributes from a JSON structure.

Now let's look at a custom block helper. Block helpers allow you to set the context before running the code contained within the block. For example, consider the following object:

{
	Name: "Parent",
	Sub: {
		Name: "Child"
	}
}

In order to display both names, you can write a block helper that runs the template once with the parent's context, and once with the child's context. Here is the helper:

Handlebars.registerHelper("BothNames", function(context, options){
	return options.fn(context) + options.fn(context.Sub);
});

And the template looks like this:

{{#BothNames this}}
	<h2>{{Name}}</h2>
{{/BothName}}

The hash tag before the helper's name tells Handlebars that this is a block helper, and you close the block not unlike you would an HTML tag. The options.fn function runs the section of template inside the block with whatever context you give it.

Now that we have the basics down, let's start creating a full demo.


Building a Site Template

You don't create a site with Handlebars.

The template we will build is for a recipe site. This will give you a good understanding of Handlebars, as it encompasses getting data from an API and passing it through a template.

Setting up a Handlebars project

We must first load our template script, but in order to do that, we need to create a new HTML file and include our Handlebars library:

<html>
	<head>
		<title>Handlebars Demo</title>
		<script type="text/javascript" src="Handlebars.js"></script>
	</head>
	<body>
		<script id="Handlebars-Template" type="text/x-handlebars-template">
		</script>
	</body>
</html>

For convenience, you can store your template in a <script /> element and load it with JavaScript. This is much cleaner than storing it directly in a JavaScript variable.

Now let's discuss how this app is going to work. First, the app connects to an API (I'm using Yummly) to pull in information on some recipes. Next, we pass this info to Handlebars and run it through the template. Finally, we replace the body section with the newly generated HTML. It's a fairly straight forward process; so, let's start by adding a second script block right before the closing body tag, and instantiate an Ajax variable:

<script>

var Ajax = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

Ajax.onreadystatechange = function(){
	if (Ajax.readyState == 4 && Ajax.status == 200)
	{
		//Parse the JSON data
		var RecipeData = JSON.parse(Ajax.responseText);
		
		//Get the Template from above
		var Source = document.getElementById("Handlebars-Template").textContent;
		
		//Compile the actual Template file
		var Template = Handlebars.compile(Source);
		
		//Generate some HTML code from the compiled Template
		var HTML = Template({ Recipes : RecipeData });
		
		//Replace the body section with the new code.
		document.body.innerHTML = HTML;
	}
}

Ajax.open("GET","Recipe.php", true);
Ajax.send();

</script>

If your site’s data regularly changes, then you might want to take a look at Handlebars.

This is the complete code for compiling and generating HTML code from a template. You can technically pass the JSON data from the API directly into Handlebars, but you run into cross origin issues. Instead of performing some sort of hack or using PHP to "echo" the data into a JavaScript variable, I decided to put all of that into a separate file: Recipe.php. So before we start building the template, let's go take a look at that PHP file.

Getting The Data

The Yummly API is pretty simple. There is no elaborate authentication system; you just have to sign up, get some credentials, and insert them into the URL. You can directly echo the data if you want to, but I want a bit more detailed info on each recipe. Therefore, I will process the data from the first API call and make a second request for every recipe. Here is the complete PHP script:

<?php
	//Empty Array to hold all the recipes
	$Json = [];
	
	$UserID = //Your ID Here;
	
	$UserKey = //Your Yummly key;
	
	//This searches Yummly for cake recipes
	$Recipes = file_get_contents("http://api.yummly.com/v1/api/recipes?_app_id=" . $UserID . "&_app_key=" . $UserKey . "&maxResult=2&requirePictures=true&q=Cake");
	
	//Decode the JSON into a php object
	$Recipes = json_decode($Recipes)->matches;
	
	
	//Cycle Through The Recipes and Get full recipe for each
	foreach($Recipes as $Recipe)
	{
		$ID = $Recipe->id; 
		$R = json_decode(file_get_contents("http://api.yummly.com/v1/api/recipe/" . $ID . "?_app_id=" . $UserID . "&_app_key=" . $UserKey . "&images=large"));
		
		
		//This is the data we are going to pass to our Template
		array_push($Json, array(
			Name => $R->name,
			Ingredients => $R->ingredientLines,
			Image => $R->images[0]->hostedLargeUrl,
			Yield => $R->yield,
			Flavors => $R->flavors,
			Source => array(
				Name => $R->source->sourceDisplayName,
				Url => $R->source->sourceRecipeUrl
			)
		));
	}
	
	//Print out the final JSON object
	echo json_encode($Json);
?>

By building your site with a Handlebars template, you can produce a full site's worth of code in only a few lines. Here is the entire template:

<script id="Handlebars-Template" type="text/x-handlebars-template">
	<div id="Content">
	  <h1>&Xi;RecipeCards 
	  	<span id='BOS'>Recipe search powered by 
	  		<a id='Logo' href='http://www.yummly.com/recipes'>
	  			<img src='http://static.yummly.com/api-logo.png'/>
	  		</a>
	  	</span>
	  </h1>
	  {{#each Recipes}}
	  	<div class='Box'>
		  	<img class='Thumb' src="{{{Image}}}" alt="{{Name}}">
		  	<h3>{{Name}} <a id='Logo' href="{{Source.Url}}"> - {{Source.Name}}</a></h3>
		  	<h5>{{getFlavor Flavors}}</h5>
		  	<h5>{{Yield}}</h5>
		  	<p>Ingredients:</p>
		  	<ul>
		  		{{#each Ingredients}}
		  			<li>{{this}}</li>
		  		{{/each}}
		  	</ul>
	  	</div>
	  {{/each}}
	</div>
</script>

Let's run through this code. The first seven lines are just the logo at the top of the page. Then for each recipe, we create a recipe 'card' with a picture, name, and ingredients.

The Yummly API returns a list of flavor data (i.e. how sweet, sour, spicy, etc..) for each item. I wrote a function helper, called getFlavor that takes this info and returns the most dominant flavor in the dish. In order for this template to work, we need to load in the getFlavor helper into Handlebars before parsing the template. So at the beginning of the second script section, add the following code before the Ajax code:

Handlebars.registerHelper("getFlavor", function(FlavorsArr){
	var H = 0;
	var Name = '';
	for(var F in FlavorsArr)
	{
		if(FlavorsArr[F] > H)
		{
			H = FlavorsArr[F];
			Name = F;
		}
	}
	return "This Dish has a " + Name + " Flavor";
});

Now, whenever Handlebars sees getFlavor, it calls the associated function and retrieves the flavor information.

At this point, you are free to play around and design the template however you wish, but you will most likely see that this process is slow. This is primarily due to the three API calls before Handlebars loads the page. Obviously, this is not ideal, but precompiling your template can help.


Precompiling

You have two different options, when it comes to Handlebars. The first is to just precompile the actual template. This reduces the loading time, and you won't have to include the Handlebars compiler with your page.

This also results in a smaller file size, but this doesn't really help in our scenario.

Our problem is the communication between the browser and the API. If you did want to precompile your template, you can download the Node.js package through npm with the following command:

npm install handlebars -g

You may need to do this as root (i.e. add 'sudo' before the command). Once installed, you can create a file for your template and compile it like so:

handlebars demo.handlebars -f demo.js

You should give your template file a .handlebars extension. This is not mandatory, but if you name it something like demo.html, then the template's name will be "demo.html" as apposed to just "demo". After naming your template, simply include the output file along with the run-time version of Handlebars (you can use the regular version, but it's larger) and type the following:

var template = Handlebars.templates['demo'];
var html = template({ Your Json Data Here });

The unless statement is...essentially an inverted if statement.

But, as I mentioned before, this doesn't really help us in this scenario. What then can we do? Well, we can precompile and output the entire file. This makes it so that we can run the template with data and save the final HTML output - caching, in other words. This drastically speeds up the load time of your application. Unfortunately, client-side JavaScript doesn't have file IO capabilities. So, the easiest way to accomplish this is to just output the HTML to a text box and manually save it. Be aware of an API's guidelines on caching. Most APIs have a maximum amount of time that data can be cached for; make sure to find that information before saving static pages.


Conclusion

This has been a quick introduction to Handlebars. Moving forward, you can look into "Partials" - small templates that can be used like functions. As always, feel free to leave a comment or question in the comment section below.

Related Posts
  • Code
    JavaScript & AJAX
    Ember.js TestingEmber components retina preview
    When I started playing around with Ember.js almost a year ago, the testability story left something to be desired. You could unit test an object without any trouble, but a unit test is only one way to get feedback when you're building a software product. In addition to unit tests, I wanted a way to verify the integration of multiple components. So like most people testing rich JavaScript applications, I reached for the mother of all testing tools, Selenium.Read More…
  • Code
    JavaScript & AJAX
    Ember Components: A Deep DiveEmber components retina preview
    Ember.js is a JavaScript MVC framework that allows developers to create ambitious web applications. Although pure MVC allows a developer to separate concerns, it does not provide you with all the tools and your application will need other constructs. Today, I'm going to talk about one of those constructs. Ember components are essentially sandboxed re-usable chunks of UI. If you are not familiar with Ember, please check out Getting Started With Ember.js or the Let's Learn Ember Course. In this tutorial, we will cover the Web Components specification, learn how to write a component in Ember, talk about composition, explain the difference between an Ember view and an Ember component, and practice integrating plugins with Ember components.Read More…
  • Code
    JavaScript & AJAX
    Working With IndexedDB - Part 3Indexeddb retina preview
    Welcome to the final part of my IndexedDB series. When I began this series my intent was to explain a technology that is not always the most... friendly one to work with. In fact, when I first tried working with IndexedDB, last year, my initial reaction was somewhat negative ("Somewhat negative" much like the Universe is "somewhat old."). It's been a long journey, but I finally feel somewhat comfortable working with IndexedDB and I respect what it allows. It is still a technology that can't be used everywhere (it sadly missed being added to iOS7), but I truly believe it is a technology folks can learn and make use of today. In this final article, we're going to demonstrate some additional concepts that build upon the "full" demo we built in the last article. To be clear, you must be caught up on the series or this entry will be difficult to follow, so you may also want to check out part one.Read More…
  • Code
    JavaScript & AJAX
    Working With IndexedDB - Part 2Indexeddb retina preview
    Welcome to the second part of my IndexedDB article. I strongly recommend reading the first article in this series, as I'll be assuming you are familiar with all the concepts covered so far. In this article, we're going to wrap up the CRUD aspects we didn't finish before (specifically updating and deleting content), and then demonstrate a real world application that we will use to demonstrate other concepts in the final article.Read More…
  • Code
    JavaScript & AJAX
    Handlebars.js - a Behind the Scenes LookHandlebars behind scenes retina preview
    Handlebars has been gaining popularity with its adoption in frameworks like Meteor and Ember.js, but what is really going on behind the scenes of this exciting templating engine? In this article we will take a deep look through the underlying process Handlebars goes through to compile your templates.Read More…
  • Code
    JavaScript & AJAX
    Prototyping With MeteorMeteor tutorial
    Meteor is far more than a quick prototyping tool, but it sure is great for prototyping. In this tutorial, we'll walk through the process of turning a simple HTML wireframe into a functional application in a surprisingly simple number of steps.Read More…