Blog

Backbone.js Tips and Tricks

Little nips and tucks can improve both the performance and the ability to manage a large front-end project. Here are some that I found helpful while working on a recent Backbone app.

Note: If you’d like to learn more tips like these, sign up for one of our Backbone.js training sessions. Also, want to help us solve problems? Quick Left is hiring. Work from our Boulder office and collaborate with our team of developers and designers to create world class applications for startups, Fortune 500 companies and everything in between. Apply now.

Use view presenters

We’ve written about the presenter pattern before, but never in the context of JavaScript. Just as presenters help keep APIs manageable, they are also useful inside a Backbone view. In your view object, simply define a new method called presenter. That should do whatever formatting you need to translate your model into an object your view can understand. Then when you call your templating function to generate the markup, pass in your presenter where you’d normally pass in your model’s JSON. This example is simplified, but presenters come in handy when you have a lot of formatting to do in order to present your template with one clean object:

Namespace.myView = Backbone.View.extend({
  render: function() {
    this.$el.html(JST['showView'](this.presenter()); 
  },
  presenter: function() {
    var presented = _.extend(this.model.toJSON(), {
      myCustomKey: "myCustomValue"  
    });
    return presented;
  }
});

When overwriting sync methods, return a deferred

Listen, you should always try to avoid overwriting a model’s built-in .save(), .destroy(), and other methods that persist data to the server. There are usually ways to tweak a model or collection’s URL in a way that lets you use .save() without overwriting it, but sometimes you just have to write your own. When you do this, make sure to return the ajax call’s deferred so you can chain a callback properly. For example, if I use .save() out of the box, I can chain callbacks when I call it like this:

myModel.save().done(function() {
  alert("Model was saved!");
});

But if you’re writing your own .save() method with a simple $.ajax() call, it’s easy to forget to return the deferred, so the above callback won’t work. Here’s a sample .save() method returning the deferred:

save: function() {
  var deferred = $.ajax({
    // ajax internals here
  });
  return deferred;
}

Use .parse() to sanitize server data

In a perfect world, all RESTful APIs would return a list of models when you request an index route and that would be that. But in the real world, APIs package these payloads in all sorts of ways. When you call .fetch() on a Backbone collection, it expects to get back a list of models not nested inside any sort of root key. To solve this, define a .parse() method inside your backbone collection. This method takes in raw server data and (if you implement it correctly) returns an array of objects. So if your API returns data like this:

myRoot: {
  model: {
    name: "Example 1"
  },
  model: {
    name: "Example 2"
  }
}

…your .parse() method would make sense of it like this, taking in the raw server data and returning the array of models Backbone needs to seed your collection:

nameSpace.myCollection = Backbone.Collection.extend({
  parse: function(data) {
    return data.myRoot;
  }
})

Use closures to keep variable declarations short

This tip applies to JavaScript more broadly, and thus isn’t limited to Backbone. We all know that letting variables slip up into the global scope is a Bad Thing. This is why it’s important to declare all variables using the var keyword at the top of each function. (Due to variable hoisting in JavaScript, variables defined anywhere in a function are hoisted to the top when it’s executed, so always declare them at the top to avoid sad surprises). In complex functions, the list of local variables at the top can grow unruly. To keep variable declarations closer to where they’re actually used in the code, use anonymous functions to give yourself an inline local scope:

myNamespace.myView = Backbone.View.extend({
  render: function() {
    var height = 110,
          width = 340,
          is_nested_row = false,
          // tons of other local variables
          render_footer = true;

          // time to format footer. keep local footer vars close to implementation
          (function() {
            var outer_height = 234,
                  background_color = red;

             $footer.height(outer_height).css('background', background_color);
          })();

  }
});

That’s it. Let us know what other tips and tricks you’ve found helpful in the comments!