Tuesday, September 30, 2014

Templating with Handlebars

When I started developing for the web using Javascript,I thought jQuery was everything.I believed that it was a nessecary and sufficient condition to accomplish everything I wanted.With this frame of mind,I tried to accomplish the task of building fairly large pieces of markup using jQuery.This is not a pleasent task by any means,the code required to build each piece of UI would be different and difficult to build and it involved concatenating strings with variables.

However,I would not be deterred,I used a few generalized methods along with a few basic higher order functions such as foreach,map and reduce to accomplish this task.The functions I wrote accepted an object with the tag,the attributes(everything from classes to custom attributes would be placed there) and an array of children objects each of which would have the same structure as the element with their children.The task of the function was to recursively render all the children starting from the deepest level of nesting and come back to the top of the tree.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var foreach = function foreach (arr, fn) {
   for (var i = 0; i < arr.length; i++) {
       fn(arr[i]);
   }
};

function mapFunc (arr, fn) {
   var res = [];
   foreach(arr, function (elem) {
     res.push(fn(elem));
   });
   return res;
};

function reduce (arr, base, fn) {
    foreach(arr, function (elem) {
        base = fn(base, elem);
    });
    return base;
};

function createElement(obj) {
  var res = $('<' + obj.el + '>');
  if (obj.attributes)   res.attr(obj.attributes);
  if (obj.style) res.css(obj.style);
  if (obj.text) res.text(obj.text);
  return res;
}
function createElementWithChildren (obj) {
   var res = createElement(obj);
   reduce(obj.children, res, function (base, elem) {
      var ans;
      if(!elem.children)  ans=createElement(elem);
      //this still goes to the object,finds the name bound in the object and uses it
      else   ans=createElementWithChildren(elem);       
      ans.appendTo(base);
      return base;
    });
   return res;
}

The idea was that I would have to then build methods for each element that we used and bring them together,which I did,successfully.I built an entire table using this.But this is rather cumbersome to create and difficult to maintain,besides I did not want to build a UI framework kind of thing(which I would have failed horribly at).

Then I discovered Handlebars,I could build templates,markup which allowed for placeholders that would be compiled into a function which when provided with the contextual data it required would transform magically into a string consisting of the markup with the placeholders filled in.

A template can be written in your html file like this:

1
2
3
4
5
6
7
8
9
<script type="text/x-handlebars-template" id="myTemplate>
    <img src="{{coverImage}}"/>
    <ul>
      <li>{{title}}</li>
      <li>{{author}}</li>
      <li>{{releaseDate}}</li>
      <li>{{keywords}}</li>
    </ul>
</script>

The {{}} holds the property in your context object that will be used here.In order to test this with some contextual data,you could try the http://tryhandlebarsjs.com/ website with just the template(code inside the script tag).

You must include the handlebars library either from a CDN or from a file.Having done that you can use:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var source=$("#myTemplate").html();
var template=Handlebars.compile(source);
var context={
  coverImage:"images/placeholder.png",
  title:"Javascript Allonge",
  author:"Reginald Braithwaite",
  releaseDate:"Unknown",
  keywords:"javascript"
}
var html=template(context); 

First you load your template,using the id you provided it with,Handlebars.compile generates a function which accepts the context using which the template is converted to html.Handlebars,unlike other templating languages such as Underscore or even your server side templating languages is a logicless.By enforcing a seperation of concerns,Handlebars makes your template easier to read and maintain unlike the methods I initially showed with a hodge podge of data,markup and jQuery all thrown together.

Does this mean that you lose a lot of functionality provided by the foreach loop in the above methods to render multiple items with the same signature?Nope,Handlebars has built-in helpers for this precise reason.

You can conditionally render parts of a template using the if,else helper and repeatedly render a part of the template using the each helper.To find out about Handlebars helpers use http://handlebarsjs.com/block_helpers.html. As the documentation suggests you can write your own helpers for Handlebars using the



1
2
3
Handlebars.registerHelper('helperName',function(context,options){
   //code here
});

For example,I was recently faced with a bit of a dilemma,how could I render any object as a simple visualization that would have a names(properties) and their corresponding values in several different formats,maybe they would be encapsulated within a single list item,maybe they would have to be displayed within a table row as two columns,maybe they will act as a label and an input's value in a form.The possibilities were endless and the ideas few.

After a little thought,I built a helper that would recieve as it's context an object,the helper would convert this object into an array of objects each containing a name and a value,which would then be used as a context for whatever template is within it.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Handlebars.registerHelper("vis_2col",function(context,options){
/*
Take the keys in an object,place them in the first column while add corresponding values to a second column,this could be a table but it could be a lot more,so allow for three elements viz.
  1.an enclosing element
  2.a name element
  3.a value element
*/
  var ret="",
      i,
      key,
      con2=[];
   if(context instanceof Object)
   {
      for(key in context)
      {   
         if(context.hasOwnProperty(key))
  {       con2.push({name:key,value:context[key]});
  }
       }
       for(i=0;i<con2.length;i++)
       {
   ret=ret+options.fn(con2[i]);
       }  
    }
    return ret;
});

I was loading loading Handlebars templates from external files,I was seemingly happy,but there was already trouble in paradise.I wanted to precompile the templates,remember the compilation step of taking your template and stuffing it into the Handlebars.compile function,the idea of precompilation is that this can be done much before(or maybe just before) you load them into your page.

Handlebars is also available as a cli tool which has been packaged as an npm module.I installed that,successfully precompiled my template and tried to use it in my web page,it was a disaster...an absolute disaster.After a few month of worrying about it with several approaches and retreats,I found the issue,npm installs the handlebars 2.0. alpha module while I was using the handlebars 1.3.0 library on the client side.This was the issue that had given me bad dreams for a long time.

I installed the correct npm module as suggested here: http://stackoverflow.com/questions/24638374/precompiled-handlebars-template-not-working
And also,I switched over to using the .handlebars extensions to avoid loading templates like a.hbs in my code.

My templates were precompiled and there was much rejoicing all around...why am I alone,hmm never mind,there was great rejoicing all around.Little did they know that evil lurked around the corner.People are indescisive,they bicker,they nitpick,they fight and they seek to control the one thing they can understand,the shiny veneer of your web application the UI.They do not understand that what may seem like dragging a few pixels around might be the straw that breaks the camel's back.

Maybe you roll your own CSS,more power( and double the work) to you.Use a framework and extend it,it is much simpler.I prefer Bootstrap for no reason except that I found a good tutorial on Youtube and heard people throwing the term responsive design around.If you are using handlebars or any other templating engine,the structure of your contextual data might change depending on the markup in the template.

Is there anything that can be done to ease the pain of having to change your template every single time someone tells you to remove a form and a table.Has the mighty templating engine or does it still have a few tricks up its sleeves?Indeed it does,and it is called partials.

Usually partials are used for seperating complex templates to make them simpler and easier to maintain.However,you can use them aggressively for conditional loading,if condition is satisfied load the dropdown,otherwise display a message.Or use it for rapid prototyping,swap partials in and out of the app instead of rewriting your template if the UI change requested of you is large enough.

There is then the matter of registering partials,a wise man once stated "A template is a partial,you fool".Following the sagely advice,I have a js file with just one line in it,it says:


1
Handlebars.partials=Handlebars.templates;

Using these techniques,I have built dynamic forms where you change your partial based on the data you obtain from the server.This is the exact same thing I was trying to accomplish using the jQuery approach stated at the beginning of this blog post.In that case,Imagine,making lightning fast UI changes using a jQuery plugin based component and creating new methods to render the html every single time.Now,heave a sigh of relief,sip your coffee,keep coding with template engines and CSS framworks that just work and experience a productivity boost.

Update:
This update is sublime text specific , if you are not a sublime text user , move on.
I am currently watching  https://code.tutsplus.com/courses/perfect-workflow-in-sublime-text-2 ,

I am in the process of discovering some of Sublime Text's most amazing and awesome features,one among them being snippets . A snippet is a pre-defined block of code that can be invoked using a keyword(sublime calls it a trigger) , I have found snippets for javascript , jQuery and several other must use libraries.

I noticed that Package Control did not have any snippets for Handlebars , so I wrote a few of my own snippets based on stuff that I frequently use.I wrote a few general purpose framework agnostic snippets.To create a new snippet,go to the menu Tools->New Snippet( in Sublime Text 2) . Save the file in a folder called Handlebars , this will work on a new file if you set the syntax to Handlebars

Invoke this by typing hbs-if and pressing Tab


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<snippet>
 <content><![CDATA[
{{#if}}
 ${1:Ok}
{{/if}}  
]]></content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabTrigger>hbs-if</tabTrigger> 
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

Invoke this by typing the tabTrigger hbs-ife and pressing if after(duh):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<snippet>
 <content><![CDATA[
{{#if}}
 ${1:ok}
 {{else}}
  ${2:nope}
{{/if}}
]]></content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabTrigger>hbs-ife</tabTrigger>
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

This is a snippet for the each helper , I find it essential to check if the array has any data before invoking the each helper with it.In case the array is empty , you can instead provide an alternate message(if you choose to):


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<snippet>
 <content><![CDATA[
{{#if ${1:content}.length}}
 {{#each ${2:content}}}
  ${3:loop}
 {{/each}}
{{/if}}
]]></content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabTrigger>hbs-each</tabTrigger>
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

This is the snippet for the Handlebars' killer feature,the partial:


1
2
3
4
5
6
7
8
9
<snippet>
 <content><![CDATA[
{{> ${1:partialName}}}
]]></content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabTrigger>hbs-p</tabTrigger>
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

This is the snippet for with,which causes the template enclosed within it to be executed using the context provided to with.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<snippet>
 <content>&lt;![CDATA[
{{#with ${1:obj}}}
 ${2:context_switch}
{{/with}}
]]&gt;</content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabtrigger>hbs-with</tabtrigger>
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

This is the snippet for the Handlebars unless helper , the unless helper checks if a condition is falsy :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<snippet>
 <content><![CDATA[
{{#unless ${1:condition}}}
 ${2:nope}
{{/unless}}
]]></content>
 <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
 <tabTrigger>hbs-un</tabTrigger>
 <!-- Optional: Set a scope to limit where the snippet will trigger -->
 <!-- <scope>source.python</scope> -->
</snippet>

Another interesting thing about snippets is that there are several stop points within it,when you hit Tab after the snippet has been expanded,it will go to each stop point till the end of the snippet is reached.In the xml code,the snippet is represented by ${number:default value}.