Making The Case For Client Side Templates

Context: We are building a single page web app with backbone. The Backbone.View object has a render method. The intent of the render method is to provide a place for the developer to render the view, by whatever means the developer wants.

Question: Why use client side templates? The reason is simple: in the javascript world, you have 3 options for building HTML:

  1. Concatenate strings
  2. Build DOM programmaticly
  3. Use a template engine

Let's pose the following scenario: Given an array of products, loop through and display the products.

var product = [
    {name: "Chicken Strips", product_nbr: "abc123"}
    {name: "Cow on a stick", product_nbr: "97208"}
];

The following 3 code blocks are psuedo code for what the render function might look like:

Concatenating Strings

var str = "<div><h1>Products</h1>";
for (i in products) {
    str += "<ul>;
    str += "<li>Name:";
    str += products[i].name;
    str += "</li><li>Nbr:";
    str += products[i].product_nbr;
    str += “</li></ul>”;
}
str += “</div>”

this.$el.html(str);

Building DOM (jQuery)

var $div = $("<div>");
$div.append("<h1>Products</h1>"
$.each(products, function () {
     $div.append(
        $("<ul>").append($("<li>").text("Name: " + this.name))
                 .append($("<li>").text("Nbr: " + this.product_nbr));
    );
});

this.$el.append($div);

Templating (Twig.JS)

this.$el.html(twig({ ref: "products" }).render({products:products}));

Products Template

<div>
   <h1>Products</h1>
   {% for p in products %}
      <ul>
          <li>Name: {{ p.name }} </li>
          <li>Name: {{ p.product_nbr }}</li>
      </ul>
  {% endfor %}
</div>

Concatenating strings gets ugly really fast. Building DOM is the least verbose, but it is error prone and incredibly difficult to maintain for anything but the simplest DOM structures. Just imagine changing the UL to table rows. It would not be fun. Templating is the most concise, easiest to read, and easiest to maintain. In this example, there is some bootstraping code that isn't shown for loading the templates from an external source (this is a topic for another post.) By loading templates as needed, you can keep your JavaScript code clean and concise. No need for your render method to be polluted with a bunch of DOM logic. Backbone.View then becomes a wrapper around your templating engine with functionality for listing to model changes and user events.

A Few Templating Engines

Disclaimer: Things move really fast. These template engine options may not be the best options out there right now. These are just the ones I have evaluated.

My Requirements for a Templatint Engine are

  1. Some logic such as loops and conditionals
  2. Flexible loading from external files or inline strings
  3. Simple syntax

Below are a few of the Templating Engines I have evaluated and how to they work to render HTML.

Underscore.JS Templates

Because Underscore.JS is a dependency of Backbone, it ships with Backbone. It is pretty simple and powerful. _.template takes the template as a string and returns a template function that will then take the variables for template and return the rendered template.

var compiled = _.template("hello: <%= name %>");
compiled({name: 'moe'});
// returns "hello: moe"

In order to get this to work in practical way inside a larger application, you will need a way to load the initial template strings. You could load the templates via ajax(not shown here) or have them inline using a script tag:

<script type="text/template" id="template-hello-world">
    <h1>Hello: <%= name %></h1>
</script>
var compiled = _.template($("#template-hello-world").html());
compiled({name: 'moe'});
// returns "hello: moe"

Conditionals and loops are achieved using JavaScript. While this is powerful, it really can be a pain in the butt:

<script type="text/template" id="template-products">
    <div>
       <h1>Products</h1>
       <% _.each(products, function(p) { %>
          <ul>
              <li>Name:  </li>
              <li>Name: </li>
          </ul>
       <% }); %>

       <% if(someConditional) { %>
           Print some string
       <% } %>
</script>

While using javascript inline is powerful, it isn't very easy to read and can get messy quick.

Mustache

Mustache is pretty neat. It has implementations in a lot of languages. Its syntax is relatively easy to read. You can load the templates in similar fashion as our first example:

<script type="text/template" id="template-hello-world">
    <h1>Hello: {{ name }}</h1>
</script>
var templateString = $("#template-hello-world").html();
Mustache.render(templateString, {name: 'moe'});
// returns "hello: moe"

Looping is little loopy (no pun intended):

<script type="text/template" id="template-products">
<div>
   <h1>Products</h1>
   {{#products}}
      <ul>
          <li>Name: {{ name }} </li>
          <li>Name: {{ product_nbr }}</li>
      </ul>
   {{/products}}
</div>
</script>

Loops are a nice feature. But what about conditionals? The docs say:

We call it "logic-less" because there are no if statements, else clauses, or for loops. Instead there are only tags. Some tags are replaced with a value, some nothing, and others a series of values.

So NOOP! No conditionals for you! It does make up for this by allowing you to pass in a function as a variable.

Handlebars

Handlebars.JS Is also a very good option. Again, Hello World:

<script type="text/template" id="template-hello-world">
    <h1>Hello: {{ name }}</h1>
</script>
var templateString = $("#template-hello-world").html();
var compiled = Handlebars.compile(templateString);
compiled({name: 'moe'});
// returns "hello: moe"

Like Mustache, Handlebars uses the a tag based system for loops and functions, but has conditionals build in:

<script type="text/template" id="template-products">
<div>
   <h1>Products</h1>
   {{#with products}}
      <ul>
          <li>Name: {{ name }} </li>
          <li>Name: {{ product_nbr }}</li>
      </ul>
   {{/with products}}
   {{#if someConditional}}
        render something
   {{/if}
</div>
</script>

Twig.JS

Twig.JS is a JS implementation of the PHP Templateing engine Twig. Twig.JS implements a subset of the PHP Twig features, but all the necessary ones are here. Like the PHP implementation, you can have custom filters, functions and tags. If you already use Twig for PHP templates, Twig.JS is a no brainer.

<script type="text/template" id="template-hello-world">
    <h1>Hello: {{ name }}</h1>
</script>
var templateString = $("#template-hello-world").html();
var compiled = twig({ data: templateString });
compiled.render({name: 'moe'});
// returns "hello: moe"

Summary

In a large JavaScript application, templates make the presentation code easier to maintain and easier to read. Most template engines are very similar but there differences are notable. Take time to evaluate your options and pick the best one for your project.

In another post, I will provide details on how to abstract away the differences in different templating engines in your Backbone.View.

Tags: backbone, js, templates, twig.js, webdev

The Problems Backbone.js Solves for Me

For my day job, I was tasked with writing some documentation regarding the way we use certain libraries to get the job done. This was a very interesting task for me, simply because I "know" the problems that Backbone.JS is solving, but I never stopped to really try to articulate it.

First a little background: We are writing a data centric single page web app. I have written single page apps using jQuery in the past. Although plain jQuery works, and IMO its better then writing Vanilla JS, it lacks in many areas. I have a one app that I still have to maintain, that essentially is a 15,000 line jQuery plug-in. What a headache!

Anyway, back to the point, the problems that Backbone.JS solves for me is three fold:

  1. Routing/Front Controller using Backbone.Router
  2. Views, Views, Views using Backbone.View
  3. Data Storage and AJAX negotiation using Backbone.Collection and Backbone.Model
  4. BONUS: Better separation of concerns and code organization.

Backbone.Router

The obvious use of the Backbone.Router component is mapping hashed URL elements to an action. Code is worth a thousand words:

var Router = Backbone.Router.extend({
    routes: {
        //define a route
        "help/:topic": "help"
    },
    //define the help action
    help: function (topic) {
        //build some model based on the the topic
        //....
        //pass the model to a view, render the view
    }
});

This is perfectly acceptable use of the Router, but when you do this, your router is going to get filled really fast with routes and actions. In addition, you will end up having the main router polluted with module specific code. For this reason, I liken my main router as more of Front Controller. The way I achieve this is I have a namespace the app call Application.Sections. This object is populated with Section objects. Perhaps some code will help:

var Router = Backbone.Router.extend({
    routes: {
        //define a route
        "sections/:section": "routeToSection"
    },
    //define the help action
    routeToSection: function (section) {
        if (typeof Application.Sections[section] === 'function') {
            new Application.Sections[section]();
        }
    }
});

Now in practice the Application.Sections objects are an extension of Backbone.View. These views take over after the router creates them. Perhaps Front Controller description is not correct, but the point is that the router becomes a thin mapper to a view layer. By modularizing the router, you can easily separate the section logic from the router, enabling the code for sections to live outside of the context of the router. This makes the app easier to maintain and organize.

Backbone.View

The Backbone.View documentation states:

Backbone views are almost more convention than they are code — they don't determine anything about your HTML or CSS for you, and can be used with any JavaScript templating library. The general idea is to organize your interface into logical views, backed by models...

This is a really important statement. Backbone.View makes no assumptions for you. They provide some initialization code that excepts a model, information for its root HTML node and some convention for delegating DOM events, but thats about it. In practice, I use views to initiate new models/collections and create sub-views.

As an example, the Section View looks something like this:

Application.Section.FooSection = Backbone.View.extend({
    initialize: function () { this.render(); },
    events: {
    'click button': function () {
            //do something when you click buttons
        }
    }
    render: function () {
        //build some dom, call template engine, etc
        ....
        //Create some subviews and collections/models specific to this section
    }
});

This is just outer layer of what Backbone.Views does for me. Lets look at some pretty ASCII art to explain the rest:

..................................................
.                                                .
.             [main section view]                .
.  -------------------------   ----------------- .
.  |       [content view]   |  |  [side view]  | .
.  |                        |  |  ............ | .
.  |                        |  |  . [widget] . | .
.  |                        |  |  ............ | .
.  |                        |  |               | .
.  |                        |  |  ............ | .
.  |                        |  |  . [widget] . | .
.  |                        |  |  ............ | .
.  -------------------------   ----------------- .
..................................................

Given the above scenario, the Main Section View is responsible for initializing the Content View and and the side view. The side view is responsible for rending the widgets. Each parent view may also be responsible for initializing Backbone.Collections or Backbone.Models for its children.

How does the view build HTML? That is a topic for another post

Backbone.Collection / Backbone.Model

Writing a single page web app is essentially just putting a UI on top of a REST API. Lets take the classic example of CRUD operations for products:

Verb URL
GET /api/products Gets a list of products
GET /api/products{id} Get a product with the given ID
POST /api/products Create a product
PUT /api/products{id} Update a product for the given ID

Backbone excels at handling AJAX negotiations with REST endpoints. Under the hood, its really just a wrapper around jQuery AJAX api, but that is exactly what is needed in order to keep our "Model" separate from our view logic. Lets see and example of how to deal with these reset endpoints using backbone:

    var Products = Backbone.Collection.extend({
        url: "/api/products"
    });
    var products = new Products();

GET /api/products

    //fetch the products from /api/products
    products.fetch();

GET /api/products{id}

    //gets product with ID 1, if it already exists in the collection.
    var p = products.get(1);
    //if it is undefined, lets add it and then fetch it
    if (typeof p === 'undefined') {
       p = products.add({id: 1});
       //fetch product info from /api/products/1
       p.fetch();
    }

POST /api/products

    //sends post to /api/products
    var newProduct = products.create({
       name: 'foo',
       product_nbr: '123'
    });

PUT /api/products{id}

    //update name attribute to foo
    p.set('name', 'foo');
    //sends PUT to /api/products/1
    p.save();

Summary

In a nutshell, Backbone.JS provides component code that allows for clean separation of concerns. By calling external objects in the Router, your router can be thin mapper to views. Views can be nested, simplifying the views and making the views more reusable. By injecting models or collections into views, you can keep data "model" logic separate from view logic. In a related post, I discuss how to render HTML with templates.

Tags: backbone, js, webdev