Chapter 5. Views and Templating

Views are the interface to your application; they’re what the end user actually interacts with and sees. In our case, views are logicless HTML fragments managed by the application’s controllers, which deal with event handlers and interpolating data. This is where it can be quite tempting to break the MVC abstraction by including logic directly into your views. Don’t succumb to that temptation! You’ll end up with senseless spaghetti code.

One of the biggest architectural changes you’ll have to make when moving server-side applications to the client side is with views. Traditionally, you could just interpolate server-side data with HTML fragments, creating new pages. However, views in JavaScript applications are somewhat different.

First, you have to transfer any data needed for the view to the client because you don’t have access to server-side variables. This is generally done with an Ajax call, returning a JSON object, which is then loaded by your application’s models. You shouldn’t be prerendering any HTML on the server side, but rather delegating all of that to the client. This will ensure that your client-side application isn’t reliant on the server for rendering views, keeping its interface snappy.

You then load that data into your views, either by creating the DOM elements dynamically with JavaScript or by using templates. I’ll elaborate on those two options below.

Dynamically Rendering Views

One way to create views is pragmatically via JavaScript. You can create DOM elements using document.createElement(), setting their contents and appending them to the page. When it’s time to redraw the view, just empty the view and repeat the process:

var views = document.getElementById("views");
views.innerHTML = ""; // Empty the element

var container = document.createElement("div");
container.id = "user";

var name = document.createElement("span");
name.innerHTML = data.name;

container.appendChild(name);
views.appendChild(container);

Or, for a more succinct API with jQuery:

$("#views").empty();

var container = $("<div />").attr({id: "user"});
var name      = $("<span />").text(data.name);

$("#views").append(container.append(name));

I’d only advocate this if the view you need to render is very small, perhaps just a couple of elements. Placing view elements in your controllers or states compromises the application’s MVC architecture.

Instead of creating the elements from scratch, I advise including the static HTML in the page—hiding and showing it when necessary. This will keep any view-specific code in your controllers to an absolute minimum, and you can just update the element’s contents when necessary.

For example, let’s create an HTML fragment that will serve as our view:

<div id="views">
  <div class="groups"> ... </div>
  <div class="user">
    <span></span>
  </div>
</div>

Now, we can use jQuery selectors to update the view and to toggle the display of the various elements:

$("#views div").hide();

var container = $("#views .user");
container.find("span").text(data.name);
container.show();

This method is preferable to generating the elements because it keeps the view and controller as separate as possible.

Templates

If you’re used to interpolating server variables in HTML, templating will be familiar. There are a variety of templating libraries out there—your choice will probably depend on which DOM library you’re using. However, most of them share a similar syntax, which I’ll describe below.

The gist of JavaScript templates is that you can take an HTML fragment interpolated with template variables and combine it with a JavaScript object, replacing those template variables with values from the object. Overall, JavaScript templating works in much the same way as templating libraries in other languages, such as PHP’s Smarty, Ruby’s ERB, and Python’s string formatting.

We’re going to use the jQuery.tmpl library as the basis for the templating examples. If you aren’t using jQuery, or if you want to use a different templating library, the examples should still be useful; the templating syntax for most libraries is very similar, if not identical. If you want a good alternative, check out Mustache, which has implementations in a lot of languages, including JavaScript.

Created by Microsoft, jQuery.tmpl is a templating plug-in based on John Resig’s original work. It’s a well-maintained library and is fully documented on the jQuery site. The library has one main function, jQuery.tmpl(), to which you can pass a template and some data. It renders a template element that you can append to the document. If the data is an array, the template is rendered once for every data item in the array; otherwise, a single template is rendered:

var object = {
  url: "http://example.com",
  getName: function(){ return "Trevor"; }
};

var template = '<li><a href="${url}">${getName()}</a></li>';

var element = jQuery.tmpl(template, object);
// Produces: <li><a href="http://example.com">Trevor</a></li>

$("body").append(element);

So, you can see we’re interpolating variables using the ${} syntax. Whatever is inside the brackets is evaluated in the context of the object passed to jQuery.tmpl(), regardless of whether it is a property or a function.

However, templates are much more powerful than mere interpolation. Most templating libraries have advanced features like conditional flow and iteration. You can control flow by using if and else statements, the same as with pure JavaScript. The difference here is that we need to wrap the keyword with double brackets so that the templating engine can pick them up:

{{if url}}
  ${url}
{{/if}}

The if block will be executed if the specified attribute value doesn’t evaluate to false, 0, null, "", Nan, or undefined. As you can see, the block is closed with a {{/if}}, so don’t forget to include that! A common pattern is to display a message when an array—say, of chat messages—is empty:

{{if messages.length}}
  <!-- Display messages... -->
{{else}}
  <p>Sorry, there are no messages</p>
{{/if}}

No templating library can afford to be without iteration. With JS templating libraries, you can iterate over any JavaScript type—Object or Array—using the {{each}} keyword. If you pass an Object to {{each}}, it will iterate a block over the object’s properties. Likewise, passing an array results in the block iterating over every index in the array.

When inside the block, you can access the value currently being iterated over using the $value variable. Displaying the value is the same as the interpolation example above, which uses ${$value}. Consider this object:

var object = {
  foo: "bar",
  messages: ["Hi there", "Foo bar"]
};

Then, use the following template to iterate through the messages array, displaying each message. Additionally, the current iteration’s index is also exposed using the $index variable.

<ul>
  {{each messages}}
    <li>${$index + 1}: <em>${$value}</em></li>
  {{/each}}
</ul>

As you can see, the jQuery.tmpl templating API is very straightforward. As I mentioned earlier, most of the alternative templating libraries have a similar API, although many offer more advanced features, such as lambdas, partials, and comments.

Template Helpers

Sometimes it’s useful to use generic helper functions inside the view, perhaps to format a date or a number. However, it’s important to keep your MVC architecture in mind, rather than arbitrarily inserting functions directly into the view. For example, let’s replace links in some plain text with <a></a> tags. This would certainly be the wrong way to go about doing it:

<div>
  ${ this.data.replace(
/((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g,
'<a target="_blank" href="$1">$1</a> ') }
</div>

Rather than injecting the function straight into the view, we should abstract and namespace it, keeping logic separate from views. In this case, we’re going to create a separate helpers.js file, containing all our application’s helpers, such as the autoLink() function. Then, we can tidy up the view with our helper:

// helper.js
var helper = {};
helper.autoLink = function(data){
  var re = /((http|https|ftp):\/\/[\w?=&.\/-;#~%-]+(?![\w\s?&.\/;#~%"=-]*>))/g;
  return(data.replace(re, '<a target="_blank" href="$1">$1</a> ') );
};

// template.html
<div>
  ${ helper.autoLink(this.data) }
</div>

There’s an added advantage: the autoLink() function is now generic and can be reused elsewhere inside the application.

Template Storage

When it comes to storing view templates, there are a few options:

  • Inline in the JavaScript

  • Inline in a custom script tag

  • Loaded remotely

  • Inline in the HTML

Some of these, however, are better at respecting the MVC architecture. I personally advocate storing templates inline in custom script tags for the reasons outlined below.

You can store templates inside your JavaScript files. This isn’t really recommended, though, because it entails putting view code inside the controller, violating the MVC architecture.

By sending an Ajax call, you can dynamically load in templates when they’re needed. The advantage of this is that the initial page load is smaller; the disadvantage is that you could slow the UI down while templates are loading. One of the main reasons to build JavaScript apps is for their enhanced speed, so you should be careful about squandering this advantage when loading in remote resources.

You can store templates inline, inside the page’s HTML. The advantage to this approach is that it doesn’t have the slow loading problem that fetching remote templates has. The source code is much more obvious—templates are inline where they’re being displayed and used. The obvious disadvantage is that it results in a large page size. To be honest, though, this speed difference should be negligible—especially if you’re using page compression and caching.

I recommend using custom script tags and referencing them by ID from JavaScript. This is a convenient way of storing templates, especially if you want to use them in multiple places. Custom script tags also have the advantage of not being rendered by the browser, which just interprets their contents as text.

If the template is defined inline in the page, you can use jQuery.fn.tmpl(data)—i.e., call tmpl() on a jQuery element:

<script type="text/x-jquery-tmpl" id="someTemplate">
  <span>${getName()}</span>
</script>

<script>
  var data = {
    getName: function(){ return "Bob" }
  };
  var element = $("#someTemplate").tmpl(data);
  element.appendTo($("body"));
</script>

Behind the scenes, jQuery.tmpl makes sure that the compiled template, once generated, is cached. This speeds things up because the template doesn’t have to be recompiled when you next use it. Notice we’re generating the element before appending it to the page; this method performs better than manipulating elements already attached to the page, making it a recommended practice.

Even if you’re rendering all the templates inline into the page, it doesn’t mean your server side should be structured like that. Try to keep each template in a separate file (or partial), and then concatenate them into the one document when the page is requested. Some of the dependency-management tools covered in Chapter 6, like RequireJS, will do this for you.

Binding

Binding is where you start to see the real benefits of view rendering on the client side. Essentially, binding hooks together a view element and a JavaScript object (usually a model). When the JavaScript object changes, the view automatically updates to reflect the newly modified object. In other words, once you’ve got your views and models bound together, the views will rerender automatically when the application’s models are updated.

Binding is a really big deal. It means your controllers don’t have to deal with updating views when changing records, because it all happens automatically in the background. Structuring your application using binders also paves the way for real-time applications, which we’ll cover in depth in Chapter 8.

So, in order to bind JavaScript objects and views, we need to get a callback that instructs the view to update when an object’s property changes. The trouble is that JavaScript doesn’t provide a native method for doing that. The language doesn’t have any method_missing functionality like in Ruby or Python, and it isn’t yet possible to emulate the behavior using JavaScript getters and setters. However, because JavaScript is a very dynamic language, we can roll our own change callback:

var addChange = function(ob){
  ob.change = function(callback){
    if (callback) {
      if ( !this._change ) this._change = [];
      this._change.push(callback);
    } else {
      if ( !this._change ) return;
      for (var i=0; i < this._change.length; i++)
        this._change[i].apply(this);
    }
  };
};

The addChange() function adds a change() function onto any object it’s passed. The change() function works exactly the same as the change event in jQuery. You can add callbacks by invoking change() with a function, or trigger the event by calling change() without any arguments. Let’s see it in practice:

var object = {};
object.name = "Foo";

addChange(object);

object.change(function(){
  console.log("Changed!", this);
  // Potentially update view
});

object.change();

object.name = "Bar";
object.change();

So, you see we’ve added a change() callback to the object, allowing us to bind and trigger change events.

Binding Up Models

Now let’s take that binding example a step further and apply it to models. Whenever a model record is created, updated, or destroyed, we’ll trigger a change event, rerendering the view. In the example below, we’re creating a basic User class, setting up event binding and triggering, and finally listening to the change event, rerendering the view whenever it’s triggered:

<script>
  var User = function(name){
    this.name = name;
  };

  User.records = []

  User.bind = function(ev, callback) {
    (this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback);
  };

  User.trigger = function(ev) {
    var list, calls, i, l;
    if (!(calls = this._callbacks)) return this;
    if (!(list  = calls[ev])) return this;
    jQuery.each(list, function(){ this() })
  };

  User.create = function(name){
    this.records.push(new this(name));
    this.trigger("change")
  };

  jQuery(function($){
    User.bind("change", function(){
      var template = $("#userTmpl").tmpl(User.records);

      $("#users").empty();
      $("#users").append(template);
    });
  }):
</script>

<script id="userTmpl" type="text/x-jquery-tmpl">
  <li>${name}</li>
</script>

<ul id="users">
</ul>

Now, whenever we alter User’s records, the User model’s change event will be triggered, invoking our templating callback and redrawing the list of users. This is pretty useful, as we can go about creating and updating users without having to worry about updating the view, which will happen automatically. For example, let’s create a new User:

User.create("Sam Seaborn");

The User’s change event will be invoked and our template will rerender, automatically updating the view and showing our new user. You can see the full model-binding example in assets/ch05/model.html.