Fork me on GitHub

Backbone patterns

This is a document with best practices in Backbone. This is a massive work-in-progress.

This document assumes that you already have some knowledge of Backbone.js, jQuery, and of course, JavaScript itself.

Model patterns

Bootstrapping data

The problem: Your application needs models to be available on page load.

Solution: Bootstrap collections and models by creating collections in an inline <script> block.

Define collection data

  <body>
    ...

    <script>
      App.photos = new Photos([
        { id: 2, name: "My dog", filename: "IMG_0392.jpg" },
        { id: 3, name: "Our house", filename: "IMG_0393.jpg" },
        { id: 4, name: "My favorite food", filename: "IMG_0394.jpg" },
        { id: 5, name: "His bag", filename: "IMG_0394.jpg" },
        ...
      ]);
    </script>
  </body>

Define your collection data in an inline script in the HTML file. If you have collections for Photos, you may be doing it this way:


Accessing instances

// Gets by ID
var photo = App.photos.get(2);

// Gets a bunch of photos based on criteria
var photo = App.photos.select(function(photo) {
  return photo.filename.match(/^IMG/);
});

To get a single Photo, instead of creating a Photo instance and using fetch(), simply pluck it from the giant collection.


In Ruby (ERB)

<script>
  App.photos = new Photos(<%= @photos.to_json %>);
</script>

In your server-side templates, you will probably be using to_json on a collection of your server-side models.


In Ruby (HAML)

:javascript
  App.photos = new Photos(#{@photos.to_json});

If you use HAML, you will need use a syntax similar to this.


In PHP

<script>
  App.photos = new Photos(<?php echo json_encode($photos); ?>);
</script>

In your server-side templates, you will probably be using json_encode() on a collection of your server-side models.


View patterns

Inline templates

The problem: if you need to use view templates in a small Backbone application, defining your templates in JavaScript code will be unwieldy and difficult to maintain.

Solution: You may need some view templates to be inline in the HTML page.

This solution has been outlined by John Resig in his blog post about JavaScript micro templating.

Defining inline templates

<script type="text/html" id="template-contact">
  <div class='contact'>
    <strong><%= name %></strong>
    <span><%= email %></span>
  </div>
</script>

You can put templates in an HTML <script> tag.

  • Change the type attribute to something else so it will not be interpreted as JavaScript.

  • Set an id so we can easily refer to it.


Using inline templates

$("#template-contact").html();
//=> "<div class='contact'>\n<strong><%= name %></str..."

template = _.template($("#template-contact").html());
//=> function() { ... }

In JavaScript, you can get the innerHTML (or jQuery's .html()) of that HTML element to fetch the raw template data. You can pass this onto Underscore's _.template to create a template function.


Integrating into Backbone

ContactView = Backbone.View.extend({
  template: function() {
    var template = _.template($("#template-contact").html());
    return template.apply(this, arguments);
  },

  render: function() {
    // This is a dictionary object of the attributes of the models.
    // => { name: "Jason", email: "j.smith@gmail.com" }
    var dict = this.model.toJSON();

    // Pass this object onto the template function.
    // This returns an HTML string.
    var html = this.template(dict);

    // Append the result to the view's element.
    $(this.el).append(html);

    // ...
  }
})

In practice, you will most likely be using this in the render() method of a view like so.


Limitations

Single-page apps only. This assumes that your Backbone application is all contained in one HTML page. If your app spans across multiple HTML pages, and each page will be needing the same templates, you may be redundantly streaming the template data to the browser uneededly. Consider using JST templates instead.

JST templates

The problem: if you need to use view templates in a small-to-large Backbone application, defining your templates in JavaScript code will be unwieldy and difficult to maintain.

Solution: You may need put the templates in a JavaScript file.

The structure

// http://myapp.com/javascripts/jst.js
window.JST = {};

window.JST['person/contact'] = _.template(
    "<div class='contact'><%= name %> ..."
);

window.JST['person/edit'] = _.template(
    "<form method='post'><input type..."
);

Your app will need to serve a dynamically-created JavaScript file that compiles your files.

A common JST file will create the JST object (in the window namespace), with each of it's members defined as template functions. In this example, we'll use Underscore's _.template, which returns functions.


<script src="http://myapp.com/javascripts/jst.js"></script>

You will then need to link to this JavaScript page in your HTML.


Using JST templates

var html = JST['person/edit']();

var dict = { name: "Jason", email: "j.smith@gmail.com" };
var html = JST['person/contact'](dict);

In your JavaScript code, simply access the JST object's members to access the views.


Integration notes

  • Rails 3.1 and above: The Rails Asset pipeline already comes with support for JST pages.
  • Rails 3.0 and below: consider using Sprockets or Jammit.
  • In Sinatra: The sinatra-backbone gem can take care of dynamically serving JST templates.

Partials

The problem: there may be parts of HTML templates that can be reused in many parts of the application. Defining them more than once is not DRY, which may make your application less maintainable.

Solution: separating these snippets into partials.

Partials are templates that are meant to be used inside other templates.

One typical use of partials is for lists where the template for list items may be defined as a separate template from the list itself.

Solution

TasksList = Backbone.View.extend({
  template: _.template([
    "<ul class='task_list'>",
      "<% items.each(function(item) { %>",
        "<%= itemTemplate(item) %>",
      "<% }); %>",
    "</ul>"
  ].join('')),

  itemTemplate: _.template(
    "<li><%= name %></li>"
  ),

  render: function() {
    var html = this.template({
      items: tasks /* a collection */,
      itemTemplate: this.itemTemplate
    });

    $(this.el).append(html);
  }
});

You can pass the template function for the partial as a parameter to the first template.

In this example, the function itemTemplate is passed onto the parameters for template().


Animation buffer

The problem: When you have events that trigger animations, they can mess up when the user clicks to fast.

The solution: Make a buffering system to ensure that animations are fired serially (one after the other) and never parallel (at the same time).

The situation

PicturesView = Backbone.View.extend({
  events: {
    'click .next':     'next'
  },

  next: function() {
    var current = this.$(".current");
    var next    = this.$(".current + div");

    if (next.length == 0) { return; }

    // Make the current one move to the left via jQuery.
    // This uses jQuery.fn.animate() that changes CSS values, then fires
    // the function supplied when it's done.
    current.animate({ left: -300, opacity: 0 }, function() {
      current.removeClass('.current');
      next.addClass('.current');
    });
  }
});

Let's say you have this innocent code that performs an animation.

One fundamental flaw here is that it assumes that .next() will only be called when it is not animating. When the user clicks "Next" while the animation is working, unexpected results will occur.


The solution

Buffer = {
  commands: [],

  // Adds a command to the buffer, and executes it if it's the only command
  // to be ran.
  add: function(fn) {
    this.commands.push(fn);
    if (this.commands.length == 1) fn();
  },

  // Moves onto the next command in the buffer.
  next: function() {
    this.commands.shift();
    if (this.commands.length) this.commands[0]();
  }
};

Here's a simple buffering solution. It provides two commands:

  • add(fn) which adds a given function to the buffer, and
  • next() which moves onto the next command.

To use this, put your animations inside an anonymous function to be passed onto add(). Be sure to trigger next() when the animations are done.


Example

next: function() {
  var current = this.$(".current");
  var next    = this.$(".current + div");

  if (next.length == 0) { return; }

  // Ensure that the animation will not happen while another animation is
  // ongoing.
  Buffer.add(function() {
    current.animate({ left: -300, opacity: 0 }, function() {
      current.removeClass('.current');
      next.addClass('.current');

      // Trigger the next animation.
      Buffer.next();
    });
  });
}

This is our example from a while ago that has been modified to use the bufferer.


Sub views

The problem: Your view code is starting to bloat as it tries to do too many things in one class.

The solution: Break it apart into smaller sub-views.

The situation

This is a common occurence if you have one giant view that takes care of the entire page. View classes may become unwieldy once they get up to 200 lines.

Solution 1: Sub views

App.ChromeView = Backbone.View.extend({
  render: function() {
    // Instanciate some "sub" views to handle the responsibilities of
    // their respective elements.
    this.sidebar = new App.SidebarView({ el: this.$(".sidebar") });
    this.menu = new App.NavigationView({ el: this.$("nav") });
  }
});

$(function() {
  App.chrome = new App.ChromeView({ el: $("#chrome") });
});

It may be wise to delegate some areas of the view to be the reponsibility of another view.

In this example, we have a view that handles the entire application "chrome." Let's break apart some of it's parts on it's render() function. Notice that we're using this.$() to select elements inside the ChromeView's element itself.


App.chrome.sidebar.toggle();

App.chrome.menu.expand();

We will then be able to access the sub-views like so:


Events

App.SidebarView = Backbone.View.extend({
  toggle: function() {
    if ($(this.el).is(':visible')) {
      $(this.el).hide();
      this.trigger('collapse');    // <==
    } else {
      $(this.el).show();
      this.trigger('expand');      // <==
    }
  },
});

All Backbone objects can emit events. To maintain the separation of responsibilities of the view classes, you may have the sub-views trigger events that the parent view would need (and vice versa).

For instance, we may implement SidebarView to trigger events when the sidebar is collapsed or expanded:


App.ChromeView = Backbone.View.extend({
  render: function() {
    this.sidebar = new App.SidebarView({ el: this.$(".sidebar") });

    this.sidebar
      .bind('collapse', this.onSidebarCollapse)
      .bind('expand',   this.onSidebarExpand);

    // ...
  }
});

And the parent view (ChromeView) may listen to them like so:


Splitting views

The problem: Your view code is starting to bloat as it tries to do too many things in one class, and making sub-views with it's child elements is not an option.

The solution: Make a sub-view with the same element.

a Solution

App.ChromeView = Backbone.View.extend({
  events: {
    'click button': 'onButtonClick'
  },
  render: function() {
    // Pass our own element to the other view.
    this.tabs = new App.TabView({ el: this.el });
  }
});

App.TabView = Backbone.View.extend({
  // Notice this view has it's own events. They will not
  // interfere with ChromeView's events.
  events: {
    'click nav.tabs a': 'switchTab'
  },
});

You can make 2 or more views that target the same element. This is useful when there are many controls in a view, but creating sub-views (with their scopes limited to a set of elements in the bigger view) may be too messy, or just not possible.

In this example, ChromeView will make a sub-view that shares the same element as it does.


Conventions

Naming convention

// Classes:
Photo
Album
Author

// Instances:
photo
myAlbum

Classes often start in uppercase letters, while instances start with lowercase letters. This is a throwback of the general Python and Ruby practice of having constant names start with uppercase letters.


// Good (CamelCase):
PhotoAlbum
albumCover

// Avoid (under_scores):
photo_album
album_cover

For names with multiple words, JavaScript often calls for CamelCase. Using underscores are discouraged in JavaScript.


Namespace convention

window.App = {
    ...
};

The convention we use puts everything in one App namespace to keep things organized properly.


App.Photo = Backbone.Model.extend({
    ...
};

Subsequent models, views, and other classes will be made in this namespace.


Models:                    App.Photo
Collections:               App.Photos
Views:                     App.PhotoView
Main router:               App.Router
Custom routers:            App.SpecialRouter

Router instance:           App.router
View instances:            App.photoView
Singleton model instances: App.photo
Collection instances:      App.photos

Some people prefer to use namespaces based on their app's name. Consider, say, BF.Photo (instead of App.Photo) if your application name is "Bacefook."


Variation: two-level namespace

Models:                    App.Models.Photo
Collections:               App.Collections.Photos
Views:                     App.Views.Photo

Some people prefer a verbose two-level version where the classes are divided into their own namespaces as well.

This is often done to make it easy to iterate over all available models, collections, and views.


RequireJS and AMD

define(function() {
  var Photo = require('models/photo');
  var Photos = require('collections/photos');
  var MenuView = require('views/menu');
  var MainRouter = require('router/main');

  // ...
});

You may adopt a Asynchronous Module Definition-style method of organization using a library like RequireJS. This will allow you to organize your modules in the require(...) way familiar to those who use NodeJS.

If you adopt an AMD library, there will be no need to use namespaces for your JavaScript classes.

See the RequireJS website for more information on RequireJS and AMD.


File naming

For applications that do not use Asynchronous Module Definition-style organization, there always seem to have 3 basic JavaScript files.

The main namespace

// app.js
window.App = {
    ...
};

This is often app.js, which defines the basic namespace.


The individual classes

// app/photoview.js
App.PhotoView = Backbone.View.extend({
    ...
});

If you use the namespacing method outlined earlier in this document, there are 2 common naming conventions for individual classes:

  • Name the files as the exact class name they contain. For instance, App.PhotoView should be stored as app/photoview.js.

  • Place each of the class types in their own folders. For instance, the PhotoView may be defined as app/views/photoview.js, or views/photoview.js.

In this approach, avoid putting code in the files other than the actual class it defines. This makes your convention predictable for the benefit of those new to your project.


The setup/glue code file

$(function() {
  // Set up some options for jQuery and plugins.
  $(document).ajaxError(function() {
    alert("There was an error.");
  });

  // Provide options for your plugins.
  $("a[rel~=lightbox]").click(function() {
    $(this).openAsLightbox();
  });

  Backbone.emulateJSON = true;

  // Initialize Backbone views.
  App.chromeView = new App.ChromeView({ el: $("body") });
  App.router = new App.Router;

  // Initialize the Backbone router.
  Backbone.history.start();
});

This is the file where you do miscellaneous things that do not belong in any of the Backbone classes:

  • Instanciate the default view
  • Initialize the Backbone Router
  • Provide options for jQuery and it's plugins

This is often named application.js or setup.js.

In larger projects, this can span multiple files. Don't be afraid to refactor it to multiple files.

This is often the only place you will want to put the onload hook $(function() { ... }).


Load order

<script src="javascripts/app.js"></script>
<script src="javascripts/app/photo.js"></script>
<script src="javascripts/app/photoview.js"></script>
<script src="javascripts/app/photos.js"></script>
<script src="javascripts/setup.js"></script>

Consider loading them in this order:

  • app.js (the namespace)
  • app/*.js (individual classes)
  • setup.js (the glue)

Anti-patterns

Here are a few common practices that I believe should generally be avoided.

$() abuse

$(document).ready(function() {
  // Initialize the router.
  App.router = new App.MainRouter;

  // Initialize the main view.
  App.dashboard = new App.Dashboard({ ... });

  // and so on...
});

// Or its shorter form:
$(function() {
  // ...
});

jQuery allows you to defer execution of code until when the DOM is fully-loaded with $(document).ready(...), or it's short form, $(...). This is useful for getting everything set up once your HTML document is ready.


// AVOID this:
$(function() {
  App.PhotoView = Backbone.View.extend({
    ...
  });
});

A common anti-pattern is to put class definitions (for views, models, and such) inside these blocks. They are not necessary.


// Consider instead:
App.PhotoView = Backbone.View.extend({
  ...
});

Your classes should be ready before the HTML DOM is. This will save you from running into problems later where certain classes may not be available at certain parts of your application.


Things outside views

Put things in your view class code as much as possible.

Event handlers outside views

App.PhotoView = Backbone.View.extend({
  ...
});

// AVOID this!
$("a.photo").click(function() { ... });

Every time you make an event handler outside a view class, consider making a new view class.


Acknowledgements

© 2011-2012, Rico Sta. Cruz. Released under the MIT License.

This document is authored and maintained by Rico Sta. Cruz with help from it's contributors. It is sponsored by my startup, Sinefunc, Inc.

To do list

  • Model associations
  • View modes
  • Nested views
  • Router entry/exit
  • View helpers