Dan Shahin
Apr 18, 2014


Handlebars.js is a lightweight but highly functional front end templating engine. It allows you to write declarative templates that take JSON input and then render that into HTML. Combined with Javascript Remoting (JSR) we now have a powerful set of tools for writing highly responsive single page apps on the Force.com platform without resorting to ugly code like this:

var firstName = 'danny',
lastName = 'shahin',
link = 'http://www.foobar.com',
//ugly code to wrap our data in HTML:
myHtml = '<a href="' + link + '">' + firstName + ' ' + lastName + '</a>';
$('div.main').html(myHtml);

Yuck! Let's avoid mixing markup with our javascript by using Handlebars templates to do all of that ugly wrapping of data in HTML for us. Here is a page that demonstrates two simple use cases for Handlebars. You could just as easily do this with straight Visualforce tags, but this is a simple example for the sake of clarity. In this screenshot we see two tables, one holds the name and birthday of a single bear while the other shows our entire population of bears.


You may notice that the following VF page has very little code in it. That's because we've stored all of our Javascript and CSS away in static resources, and even packaged up our Handlebars templates as an Apex component. The minimalistic page is just waiting to have templates rendered and inserted into simple div elements. This is a big change from the typical VF page that is littered with often obscure tags and clunky onClick functions mixed right in.

<apex:page showHeader="true" sidebar="false" doctype="HTML-5.0" title="handlebears" controller="HandlebearsController">
    <head>
        <apex:stylesheet value="{!URLFOR($Resource.BearsCSS)}"/>
    </head>
    <div id="main"/>
    <div id="bearList"/>
    <!-- handlebars.js templates are stored in a component for use in other pages -->
    <c:BearTemplates/>
    <!-- external js libraries loaded at the bottom of page -->
        <script src="https://code.jquery.com/jquery-2.1.0.min.js"/>
        <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.2/handlebars.min.js"/>
        <script src="{!URLFOR($Resource.HandlebearsJS)}"/>
</apex:page>
\

Next we can look at the Handlebars.js templates that we have stored for convenient reuse as an Apex component. Now we can use these same templates in any VF page we need them in. Code reuse can be a wonderful thing! Here we see two templates. The first renders a table from a single Bear object, showing Papa Bear alone in his own table. The next template show how we can use the {{#each}} directive in the template to loop over a list of Bear objects, rendering one row per bear returned by our JSR method. Notice how the list template makes use of the {{this}} variable to refer to a single bear. Much like the "var" attribute in an Apex:Repeat tag, "this" provides a way to reference attributes in each object in our list.

<apex:component >
<!-- template for a single bear object -->
    <script id="bear-template" type="text/x-handlebars-template">
        <table class="bears">
            <tr><th colspan="2">Just one Bear</th></tr>
            <tr>
                <th>name</th>
                <th>birthday</th>
            </tr>
            <tr>
                <td><a href="/{{id}}">{{name}}</a> </td>
                <td>{{birthday}}</td>
            </tr>
        </table>
    </script>
    <!-- template for a list of bear objects -->
    <script id="bear-list-template" type="text/x-handlebars-template">
        <table class="bears">
            <tr><th colspan="2">All the Bears</th></tr>
            <tr>
                <th>name</th>
                <th>birthday</th>
            </tr>
            {{#each}}
            <tr>
                <td><a href="/{{this.id}}">{{this.name}}</a></td>
                <td>{{this.birthday}}</td>
            </tr>
            {{/each}}
        </table>
    </script>
</apex:component>

The layout is pulled together in our javascript resource. Here we use JQuery to find our template sources and compile them into functions that when combined with JSON data returned by JSR methods, renders our tables using very little code to do so. Less code means more time for everything else like testing, styling and of course, Bear watching.

//allow console.log on earlier IE versions
if (!window.console) console = {log: function() {}};
$(document).ready(function(){
    var singleBearTemplate = Handlebars.compile($("#bear-template").html()),
    multiBearTemplate = Handlebars.compile($("#bear-list-template").html()),
    renderBear = function(context){
        var html = singleBearTemplate(context);
        $('#main').html(html);
    },
    renderList = function(context){
        var html = multiBearTemplate(context);
        $('#bearList').html(html);
    }
    HandlebearsController.showPapa(function(result,event){
        if(event.status){
            //use the JSR result as context for a template
            renderBear(result);
        }else{
            //friendly message for user
            alert('Could not fetch Papa.');
            //detailed message in console for debugging
            console.log(event.message);
        }
    });
    HandlebearsController.listBears(function(result,event){
        if(event.status){
            //use the JSR results as context for a template
            renderList(result);
        }else{
            alert('Could not fetch bears.');
            console.log(event.message);
        }
    });
});

Our Apex controller ends up being a container for a couple of JSR methods. These are very easy to write, use and especially to write unit tests for.

global with sharing class HandlebearsController {
    @RemoteAction
    global static Bear showPapa() {
        Bear__c bear = [Select Name, CreatedDate from Bear__c where Name = 'Papa Bear'];
        return new Bear(bear);
    }
    @RemoteAction
    global static Bear[] listBears() {
        Bear[] bears = new Bear[]{};
        //note: not limited to 1000 rows like Visualforce
        //JSR methods can return up to 10 MB of JSON data
        for(Bear__c bear : [Select Name, CreatedDate from Bear__c order by Name limit 5000]){
            bears.add(new Bear(bear));
        }
        return bears;
    }
//a wrapper class for the bears makes for cleaner templates
        global class Bear {
            public String name {get;set;}
            public String birthday {get;set;}
            public String id {get;set;}
            public bear(Bear__c bear){
            this.name = bear.name;
            this.birthday = bear.CreatedDate.format('MM/dd/yyy');
            this.id = bear.id;
        }
    }
}

If this is all that Handlebars.js could do for us, I'd still recommend it, but the fact is I have barely scratched the surface of this powerful library. In a future post I will delve into more complex examples that will demonstrate more advanced features, but you can skip ahead by reading the excellent documentation on the Handlebars website.

Accelerate your ISV Work.com strategy

Stay in the know on all things SaaS and Salesforce with The Decoder.

Join Our Newsletter

See what our newsletter has to offer:

Check out a recent copy here!

Join Our Mailing List