The Golden Middle Path - a blog by Amit K Mathur

Handlebars

Handlebars is a templating system – a language to write (mainly HTML) templates.

A template is a document with placeholder variables. A templating system can take a template, list of values for the variables used in the template and produce a final document. (Its like mail merge if you know what that is)

Input: template + variable values
Output: document produced by replacing the variables in the template with the values

Handlebars templating system has two main defining features:

  • It is logic-less. i.e. there are no if-then-else, no for-loops etc. That makes it easy for even non-programmers to write Handlebar templates.
  • It supports helpers. Using helpers, it is easy to extend Handlebars. There are two types of helpers: regular helpers and block helpers.

Although Handlebars compiler is available in several languages, it is mainly used with Javascript.

A simple example

If you have a template like this:



  <div class="product">
    This is a {{product}}.
    <span class="price">
     price: {{price}}.
    </span>
  </div>


And if your data looks something like this:

var data = {product: "shirt", price: 36.99};

Then the final document will be this:



  <div class="product">
    This is a shirt.
    <span class="price">
     price: 36.99.
    </span>
  </div>


Get your hands dirty

Install node.js


  $ git clone https://github.com/joyent/node.git
  $ cd node
  $ make
  $ sudo make install
  $ export NODE_PATH=/usr/local/lib/node_modules # also add it to ~/.bashrc

Install npm – npm is package manager for node.


  $ curl -k https://npmjs.org/install.sh | sudo sh -

Install Handlebars node package/module


  $ sudo npm install -g handlebars

Start a node session and try out Handlebars


  $ node
  > var Handlebars = require("handlebars"); // load Handlebars module
  > var template = "This is a {{product}}. Its price is {{price}}";
  > var compiled_template = Handlebars.compile(template);
  > var data = {product: "shirt", price: 36.99};
  > compiled_template(data) // this will print: 'This is a shirt. Its price is 36.99'

Paths

Handlebars makes it easy to access data even when the JSON hash is
hierarchical. For example, if your data were structured like:

var data = {"product": {"name": "shirt", "description" : "a nice summer shirt"}, price: 36.99}

you can write the template:

var template = "This is a {{product.name}}. Its price is {{price}}";

Blocks

Handlebars supports something called blocks (borrowed from Ruby blocks). Have
a look at this template:



  <ul>
    {{#each products}}
      <li>
        <h3>{{name}}</h3>
        <p>{{description}}</p>
      </li>
    {{/each}}
  </ul>


If you feed it the following data:


{
  products: [
    {name: "The Mythical Man Month", description: "Fred Brooks"},
    {name: "Godel Escher Bach", description: "Douglas Hofstadter"},
    {name: "More Programming Pearls", description: "Jon Bentley"}
  ]
}

It will produce the following output document:



  <ul>
    <li>
      <h3>The Mythical Man Month<h3>
      <p>Fred Brooks</p>
    </li>
    <li>
      <h3>Godel Escher Bach<h3>
      <p>Douglas Hofstadter</p>
    </li>
    <li>
      <h3>More Programming Pearls<h3>
      <p>Jon Bentley</p>
    </li>
  </ul>


There are several builtin block helpers: with, each, if and unless.

Defining your own helpers

Handlebars allows you to define your own helpers. Helpers are of two types: Regular Helpers and Block Helpers.

Regular Helpers


  Handlebars.registerHelper('availability', function(prod_data) {
    return prod_data.has_stock ? "In Stock" : "Out of Stock";
  });

  var data = { product: {name: "The Mythical Man Month", has_stock: true, price: 34.99 } };
  var source = "{{product.name}} is currently {{availability product}}";
  var template = Handlebars.compile(source);
  template(data); // Would render: 'The Mythical Man Month is currently In Stock'


When calling a helper, you can pass paths (like above) or strings (shown below) as parameters.



  var source = '{{upcase "in stock"}}' //Assuming you have defined a helper "upcase"


Block Helpers

Handlebars allows you to define your own block helpers to augment the built-in ones. Lets say you want to define a helper called prettify.



  Handlebars.registerHelper('prettify', function(options) {
    return '<div class="highlight">' + options.fn(this) + '</div>';
  });

  var source = "<p>{{#prettify}}{{name}}{{/prettify}}</p>";
  var template = Handlebars.compile(source);
  var data = { "name": "Poor Little Rich Slum" };
  template(data); // renders: <p><div class="highlight">Poor Little Rich Slum</div></p>


Note that a helper, either regular or block helper, can take 0 or more arguments. In the above example availability helper takes 1 argument.

If a helper is used as a block helper, it will always get a hash as the last argument, called options in the above example. This options hash will contain a method called fn which behaves exactly like a compiled Handlebars template for the little template that’s enclosed between the block. (Ruby programmers will notice the similarity with ruby blocks)

Also, note that inside a helper, this always refers to the current context or the data.

Partials

You can register a template as a partial, which will be used by Handlebars when it encounters a partial ({{> partialName}}).


// Register a partial
var item = '<li><span id="{{id}}">{{name}}</span></li>';
Handlebars.registerPartial('menu_item', item);

// Optionally, you can compile the partial before registering.
// var compiled_item = Handlebars.compile(item);
// Handlebars.registerPartial('menu_item', compiled_item);

var source = "<ul>{{#each menu_list}}{{> menu_item}}{{/each}}</ul>";
var template = Handlebars.compile(source);
var data = { "menu_list": [{id : "home", name : "Home"}, {id : "about", name : "About Us"}, {id : "contact", name : "Contact Us"}] };
template(data); // this will render: <ul><li><span id="home">Home</span></li><li><span id="about">About Us</span></li><li><span id="contact">Contact Us</span></li></ul>


Notes

  • Handlebars HTML-escapes all variables by default. To not escape, use triple moustaches {{{ }}} or new Handlebars.SafeString("abc").
  • Handlebars is an extension of Mustache and all Mustache templates should work with Handlebars compiler.
  • Partials can either be string templates (as shown above) or they can be compiled template functions as well (as shown in the comment in the example above).
  • You can add comments to your templates with the following syntax:


  {{! This is a comment }}


  • Handlebars support pre-compiling. It is much faster to precompile the templates. Also, if you precompile, you only need handlebars.runtime.js to instantiate your templates into documents.

References

Share:

Post a comment


(Formatting help)