Async Template Loading With Backbone
This is my fifth post in a series on Backbone.js. Here are the first 4 in case you need to get caught up:
- Hey, check out my Backbone
- Aligning my Backbone
- Backbone Routers Are Configuration
- Grokking Backbone Views
In my previous post, I dug into the guts of how Backbone Views work, how to render them using client-side templating, and extending off of Backbone.View’s prototype. This post is going to build off of the previous one. Today, we’re going to talk about lazy-loading your client-side template HTML files asynchronously, caching, pre-compiling them, and why these are usually all good ideas. But before we get into the Why’s and How’s, I want to talk about the Why Not’s.
When async templates don’t make sense
I generally subscribe to Jeff Atwood’s idea that the best code is no code. Jeff probably didn’t originate that, but I’ve read it in several of his blog posts, and it has stuck with me throughout my career. So when does loading your templates asynchronously via AJAX, pre-compiling and caching them not make sense? When you really want to avoid unnecessary code. You can just as easily stick your templates inside inline <script> tags, hang them off of properties on your views, or pre-load them with an Asynchronous Module Definition (AMD) framework like Require.js.
Additionally, in the event that you’re writing an app that requires some sort of offline support, lazy loading anything from the server isn’t really an option. You really need certain features at your fingertips as soon as the page loads. I know that’s kind of a no-brainer, but it’s definitely worth mentioning.
So why would I use this then?
Because it’s faster! Lazy loading allows you to only load the templates that your application needs at any given time. This helps lighten the initial load of your application’s content. It also helps reduce the amount of HTTP requests initially sent back to the server as the page loads if they’re pre-loaded with something like Require.js.
You can also pre-compile your templates to optimize their performance when you lazy load them. While pre-compilation isn’t unique to this approach of template loading, it does give you a nice unified place to put this functionality.
Finally, this approach also give you a single place to manage initialization of any partials your app might need to care about. It allows you to encapsulate how you actually render your templates. You can use whatever templating library you like (Underscore, Mustache, Handlebars, etc). Now you can easily change this implementation in a single place, rather than sprinkling this concern throughout your views.
The Usage
In my last post, I showed some examples of how Backbone Views can render their content from a hard-coded string stuffed in a template property on the View object. You’ll also often see templates stored as HTML fragments inside <script type="text/html"> blocks that can be selected by ID from jQuery. Both of these are nice for quick and dirty implementations. However, I typically like to store my templates in separate HTML files located in a /templates folder just to keep things organized a little better.
Here’s what our usage would look like if we refactored the previous code example to use a TemplateManager object to lazy-load our templates for us.
FooView.js
var FooView = Backbone.View.extend({
tagName: 'li',
className: 'foo',
// `template` is now the name of a file (sans extension) instead of the HTML
// fragment itself
template: 'foo-list-item',
events: {
'click .fooButton': 'fooClicked'
},
initialize: function (options) {
this.model = options.model;
this.model.on('change', this.render);
_.bindAll(this);
},
render: function () {
var that = this;
// Note that we're still doing much the same thing to create the HTML content
// and stuff it into the DOM, but we're doing it inside this callback.
TemplateManager.get(this.template, function (tmp) {
var html = tmp(that.model.toJSON());
that.$el.html(html);
});
// And we're still returning `this`, but it's highly likely that our `render`
// method will return *before* the HTML has been placed into the DOM.
return this;
},
fooClicked: function (e) {
alert('ouch!');
return false;
},
});
Remember how I talked earlier about rendering templates asynchronously? Yep. That’s what’s happening here. Almost every time, our view.render() function will return before the HTML content is actually placed onto the page. You may ask, “How is this possible? Why would you do it that way”. The async nature of this approach certainly adds a bit more complexity, but it makes up for the added complexity in speed. Our app won’t block execution whilst waiting for that HTTP request to finish, and when the content has been loaded, we’ve provided a nice callback so our View knows what to do with it when it completes.
Let’s look at the implementation of a TemplateManager to see how this works.
The Implementation
If you’re a server-side coder, think of the TemplateManager as a sort of static class or object that just has a handful of utility methods or properties.
TemplateManager.js
var TemplateManager = {
// Here, we're keeping an object literal around to act as a hash table, and we'll
// be using it to cache each template that gets loaded from the server.
templates: {},
get: function (id, callback) {
// Can we find this template in the cache?
if (this.templates[id]) {
// Yes? OK, lets call our callback function and return.
return callback(templates[id]);
}
// Otherwise, lets load it up. We'll build our URL based on the ID passed in.
var url = '/templates/' + id + '.html',
// And use a handy jQuery library called Traffic Cop to handle marshalling
// requests to the server. This will prevent multiple concurrent requests
// for the same resource.
promise = $.trafficCop(url),
that = this;
// Wire up a handler for this request via jQuery's promise API
promise.done(function (template) {
// `template` is a string of HTML loaded via `$.ajax`. So here, we
// can take the opportunity to pre-compile it for performance. When we
// pre-compile a template, it returns a function that we can store in our
// cache for future use.
var tmp = Handlebars.compile(template);
that.templates[id] = tmp;
callback(tmp);
});
}
};
You probably noticed in the above code example my usage of $.trafficCop. It’s a super useful jQuery plugin that acts as a proxy for $.ajax. It comes in really handy when you have several concurrent requests coming in for the same resource. A great example of this would be rendering a list view. Let’s assume this list view might have a collection of child list item Views that require their templates to be loaded as well. TrafficCop would prevent more than one request from actually making it to the server. It stores the callbacks to each request for a resource and will call them back in the correct order.
I picked it from from Derek Bailey. Check out the repo on GitHub for more info on TrafficCop.
Handling Partials with TemplateManager
In addition to lazy-loading templates on the fly, I also will use TemplateManager to pre-load and register partials when our app is initialized.
var TemplateManager = {
// ...
// You could stash an array of well known partials that need to be rendered
// and registered with your Templating engine of choice.
partials: [
{ name: 'fooForm', template: 'foo-form' }
],
registerPartials: function (callback) {
var that = this;
_.each(this.partials, function (partial, index) {
// As we're iterating over our partials, we can call our
// existing `get` function to pre-compile and cache them.
that.get(partial.template, function (tmp) {
Handlebars.registerPartial(partial.name, tmp);
if (index + 1 === that.partials.length) {
callback();
}
});
});
}
};
Now when we’re bootstrapping our app, we can pre-load all of our partials before starting our router.
app.js
$(function () {
TemplateManager.registerPartials(function () {
var router = new FooRouter({ model: someData });
Backbone.history.start();
});
});
The benefit of this is a little bit more specific, but it enables you to call partials by name in your templates to encapsulate common HTML fragments.
new-foo.html
<div class="new-foo">
<h3>Fill out the following form to create a new foo</h3>
{{> fooForm}}
</div>
edit-foo.html
<div class="edit-foo">
<h3>Editing foo: '{{name}}'</h3>
{{> fooForm}}
</div>
That basically covers it. As you can probably tell from my examples, I’ve been using Handlebars quite a bit for my templating. So far I’m becoming a fan. It’s got a good balance of functionality via partials, blocks, helpers, etc. These help keep imperative code out of your template markup. And pre-compiling helps boost performance a lot (check out Derek Bailey’s blog post on this subject for more details).
It’s also worth mentioning that this technique is implemented in Derek Bailey’s Backbone.Marionette plugin. I haven’t had a chance to kick the tires on it yet, but I’ve heard great things about it.
So what do you guys think? Is the added speed and flexibility of async template loading worth it to you? Do you plan on using this technique in any of your single page apps?

