Intro To Backbone.js

Before you begin I should mention this blog post was written over a year ago. Since then I’ve learnt many important lessons. Some of the content may be outdated.

In the last couple of years the world of Client Side MV* (Model View whatever…) has really grown and matured. Addy Osmani’s TodoMVC does a great job of highlighting the differences between the ever growing list of frontend frameworks out there.

I began working with Backbone in 2012 when single page applications were becoming mainstream. I was given the task of rebuiling a tool making sure it’s fast, responsive and maintainable.
After some careful consideration, I decided to use Backbone because of its simplicity and flexiblity. The whole code base, including the comments, is 1496 lines. That’s why it’s often called a library rather than a framework.

If you’ve gotten started with Backbone, or any MV* framework, you’ve probably had to adapt and consider things you haven’t touched before in frontend development.
In this post I’m going to cover some of the more common challenges you might encounter during development, and how I went about tackling them.

Naming Convention

As per the backbone patterns I use camelCase for instances and Capitalize the constructors. That’s how I have the application namespaced too; the whole app is namespaced under the app namespace

1
2
3
4
5
6
7
8
9
10
11
12
window.app = {
 // the constructors (aka classes)
 Views : {},
 Models : {},
 Collections : {},

 views : {},
 models : {},
 collections : {},

 utils : {}
}

The disadvantage with this is namespace pollution. Creating an object in the global object is considered bad practice. There are solutions for this, like using require.js however that’s beyond the scope of this post.

Subviews

How to handle subviews was one of the most important questions as our app grew in complexity.

One of the main concerns I had here was memory management (which I’ll talk more about later). [Backbone Fundementals] (http://addyosmani.github.com/backbone-fundamentals/#nesting-what-is-the-best-approach-for-rendering-and-appending-sub-views-in-backbone.js) covers this pretty nicely. If you initialise all the subviews in the parent view’s ‘initialize’ function and then render the subviews in an ‘onRender’ function, which is called when our ‘render’ event is triggered:

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
var OuterView = Backbone.View.extend({
   initialize: function() {
       this.on('render', this.onRender);
       this.subViews = {};
       this.subViews.innerView = new InnerView();
   },

   render: function() {
       this.$el.html(template);
       this.trigger('render');
       return this;
   }

   onRender: function() {
     this.$el.append( this.subViews.innerView.render().el);
   }
});

var InnerView = Backbone.View.extend({
   render: function() {
       this.$el.html(template);
       return this;
   }
});

The great thing about this is that it leaves us with references to all the subviews which we can later clean up.

Memory management

It’s really easy to lose track of objects and create Zombie views in a single page application, which can lead to some hard to find memory leaks.

Derick Baily, a noteable Backbone developer and creator of the Marionette framework, covers this issue in his article Zombies! RUN!.

This has been widely discussed in the Backbone community. There are a few different approaches out there, what I went for in the end was to create a universal dispose function.

This ‘dispose’ function is called by the router whenever the main view is changed.

This is done using Backbone events in the router. Every route triggers an event passing the main view of the route; if it’s different to the currentView, as saved by the router, the dispose function is called on the main view.

Here is an example, heavily inspired by Chaplin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Backbone.View.prototype.dispose = function() {

   if(_.isObject(this.modelBinder)) {
       this.modelBinder.unbind();
   }

   if(_.isObject(this.subViews)) {
       _.each(this.subViews, function(presenter, i) {
           presenter.destroy();
           delete this.subViews[i];
       }, this );
   }

   if(_.isObject( this.model ) )  {
       delete this.model;
   }

   if(_.isObject( this.collection ) )    {
       delete this.collection;
   }

   this.remove();
}

It’s worth noting that this function is added to the Backbone View prototype. If you find your self adding more functions to the Backbone.View.prototype, it’s probably time to create a base view, which all your views extend.

A bonus tip: listenTo and stopListening are two new useful features added in Backbone 0.9.9. They provide an easy interface to remove bindings to other objects. This is great when you have a lot of bindings to other objects ( e.g. Models, Collections, Subviews, Mediator ). The way this works is by keeping track of all the events the object is listening to in other objects.

Also worth mentioning that the modelBinder plugin can be used for two-way model-view binding. This is extremly useful if you are dealing with many forms and provides a declarative way to define these bindings.

The unbind function, direct from the documentation:

The unbind() function will un-register from the model’s change events and the view’s jQuery change delegate.

Recently NYTimes also released their own model-view binding solution for Backbone. Although I haven’t tried it yet, Backbone.stickit seems to be in active development and may be a good alternative.

Complex models

When working with a REST API sometimes the reponse includes a nested object that contains some extra metadata, Backbone assigns this as a property by default, but unfortunetly no events are triggered when the sub-object changes. To solve this issue I create a submodel upon initialization and just update it on parse.

For example, let’s say we were fetching a comment for a photo in a photosharing website and this was the json response:

1
2
3
4
5
6
7
8
9
10
11
12
{
 "id": 13685794,
 "created_at": "2011/04/06 14:55:30 +0000",
 "user_id": 1505645,
 "body": "great shot!",
 "user": {
   "id": 1505645,
   "username": "joeyl",
   "name": "Joey Lawrance",
   "avatar_url": "http://somefilehosting.com/avatars-000003153897.jpg?"
 }
}

This is how we could set the user submodel after a fetch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Comment = Backbone.Model.extend({
 urlRoot : '/api/comments/',

 initialize : function(options) {
   this.set({ user : new UserModel() });
 }

 parse : function(response) {
   this.get('user').set(response.user);
   response.user = this.get('user');

   return response;
 }

});

Promises

As a kid I used to ask my parents for ice cream when there was no ice cream shop around. My parents would usually promise me, that as soon as we saw a shop, I would get an ice cream. Until we found an ice cream shop, I would be left with that promise; as soon as I got the ice cream the promise would be ‘resolved’ and I would say “thank you”. If we didn’t end up finding an icecream shop I would usually fail gracefully by demanding cake. If we were to write a program that would react this way, promises could be of help.

When you think about asynchronous programming, you make requests (typically AJAX), and react acordingly. Promises is an interface helping the developer manage asynchronous callbacks and manage possible cases/outcomes. It has been included in jQuery since version 1.5. jQuery’s implementation is called Deferreds objects and is based on the CommonJS design.

If you have been writing code that looks like this:

1
2
3
4
5
6
7
8
9
myModel.fetch({
 success: function () {
   myOtherModel.fetch({
     success : function() {
       myView.render();
     }
   });
 }
});

You will probably benefit from using jQuery’s Deferred Object.

So assuming we wanted to fetch a model and while it’s fetching, show an animated loader gif. In both cases of success and failiure we would want to hide the animated gif. So without promises it would look like this:

1
2
3
4
5
6
7
8
9
10
myModel.fetch({
 success: function() {
   $('#ajax-loader').hide();
   drawTable();
 }
 error: function() {
   $('#ajax-loader').hide();
   showError();
 }
});

Not very DRY, so how about this:

1
2
3
4
myModel.fetch()
 .done(drawTable)
 .fail(showError)
 .always(function() { $('#ajax-loader').hide(); });

Calling fetch on a model in Backbone returns a jqXHR object.From the jQuery documantation:

The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise.

Since jQuery has both promises and deferred objects, you might ask your self what is the difference.

This can be explained using an example: If you are implementing your own deferred object for a function that loads a notification waits a minute and then hides it. The function would create a defered object which could later resolve or reject depending on how it went. The part of your code which called that function will want to define the callbacks for the different cases so it needs access to the done,fail,always. However this part of your code cannot decide if the operation was successful or not. There for it doesn’t have access to the resolve or reject functions.

For more on promises in jQuery, we recommend this article.

Thanks to the great Backbone community the project has really matured over the last year, with a rapidly growing community and over 17,000 stars on Github. Answers can be easily found on stackoverflow.

Backbone is a great entry point and the most popular framework in large part thanks to its simplicity and flexibility. Of course there are many other promising frameworks. AngularJS for example is also gaining popularity due to its slick abstractions and declarative approach or react.js which has a big focus on performance and the ability to render on the server. In fact there’s no magic bullet when it comes to frontend development.