A Conceptual Understanding of Backbone.js For The Everyman

- Jonathan Otto email me: jonathan.otto@gmail.com

Official explanation

"Backbone.js gives structure to web applications" ... "models with key-value binding" ... "collections with a rich API of enumerable functions, views with declarative event handling" bla bla bla.

The everyman's explanation

You don't want this for small web apps, and if you're a Rails developer, you, much like DHH might prefer the simplicity and Axeness of Rails partials rendered via AJAX.

... but if you want some structure in large data driven web apps, with minimal page refreshes, client side templating and JSON over the wire...

You've written AJAX apps with jQuery callbacks like this:

$.ajax({success:function({//DOM manipulation})})
But with Backbone.js, you just manipulate Backbone models (JSON objects) via a set method:
some_model.set({name: 'new some thing'})
which will "trigger" 2 "EVENTs!!" on that model: "change" and "change:name" and then, in a "view", you can monitor for those events and just repaint (as opposed to DOM manipulation in a callback) the relevant view e.g.
this.model.on("change", function(){this.render()})
In THEORY, Backbone.js forces you to compartmentalize the DOM manipulation to wait for events so future you and other developers can easily adjust the HTML and CSS without breaking the Javascript that syncs with storage adapters or REST APIs.
The purpose of this document is to articulate how to achieve that sweet spot of abstraction: tying the DOM to events

Backbone creator, Jeremy Ashkenas explanation

In the end, instead of peeking into a blob of JSON, tweaking the DOM manually, and making a $.ajax call, the hope is that you'll be able to write:
note.save({title: "Lorem Ipsum"});
... and all of the UI currently referencing the note automatically updates, and the changes are saved to the server.

The nitty gritty

Though Backbone.js documentation suggests there's "There's More Than One Way To Do It", what follows is some background on that basic concept of tying the DOM to events.

It's really just JSON objects and "render" methods

Backbone.js is made up... mostly of "models" and "views" that are so damn simple you'll wonder why Backbone exists in the first place. The Models are really just javascript key pairs/objects/dictionaries/hashes (or whatever) with the addition of a getter setter API that triggers "events" (which is where the utility of Backbone stems from). The Views essentially only have a "render" method that you write yourself with the objective of populating a Javascript template with a Javascript object using any templating language you choose (or if you were crazy you could just call a bunch of $(css_selector).html(this.model.get('some_attrib'))) because as long as that DOM manipulation is granular and responds to events, you're doing it right.

Bind all views to change events on models, and/or reset/add/remove events on collections

The critical concept to understand, again of course, is tying/binding your views to models. You do that simply by "binding", or "on" model change events in the View constructor (which get triggered from the model getter setter methods) to the view's render method. If you follow this pattern then you get the true benefit of abstracting away model manipulations and server syncing from the view layer. Ultimately, as Jeremy Ashkenas points out above, we want to get to the point where we can call some_model_object.set({key: val}), or some_model_object.destroy(), or some_model_object.fetch() and the view(s) will adjust accordingly. If you don't follow this pattern, you'll end up with "spaghetti" code.

Aggregate data and don't expect Backbone to mirror your schema

The dirty secret is that you _may_ end up writing A LOT of models and views using this pattern (unless your application is very simple) because you'll want to have a granular level to minimize repaints and server syncs. This is especially true if you have aggregate data (sums, counts or other compilations). Don't expect your Backbone views, and really, even Backbone models to match up exactly with your database schema. For example, if you have a database table, "blogs", you might have a Backbone collection: Blogs made up of Blog models AND another model BlogAggregateData. The BlogAggregateData may change whenever someone adds/removes a comment to a blog (so then you're syncing just the BlogAggregateData model as opposed to maybe a Blog model with all the blog attributes and aggregate data attributes)

Gotchyas

Don't loop over collections in your templates

If you want to do this, you might as well just use HTML partials and AJAX. Instead, use a Backbone View that renders its own views (the granularity is what minimizes server syncs and page refreshes). This is recursive, you can repeat this pattern until there is no more associated data to loop over. If your User model has many blogs, then create a Backbone View and pass in a User model object AND a collection of blog objects.
There are of course exceptions where it makes sense to loop in the template: e.g. you have an array of aggregate data whose rows can't easily be tied to individual schema objects.

When creating views, pass models and collections

When you create a Backbone View object, you'll want to pass it ONE Backbone model object and the collection it belongs to (most of the time, a model SHOULD belong to a collection), OR if you have a list, pass a Backbone Collection. If you pass a Collection to a View, then that View's render method will need to iterate that Collection and instantiate new Views that each get passed a Model (so you can avoid looping in the template). Maybe you have aggregate data that doesn't flow with a simple CRUD REST API, then simply make a new Backbone Model, like BlogAggregateData, that's tied to an API endpoint that provides that data.

Use the same models everywhere

Don't instantiate new Models if you already have a collection that contains that model - just pull the model out of the collection (collection.get(dbId)). Imagine a 2-pane UI where you have a list of blogs on the left, and a pane on the right to show detail and editing controls on any one blog. When manipulating a blog model in the right pane, you'll need it to reference the same Backbone.js model in memory as the row in the list on the left does so changes will be reflected in both panes.

Building $el in memory AND Canvas element

Backbone.js views can be passed an existing DOM element, or it can build one in memory that doesn't already exist in the DOM (the latter is always the case when you iterate collections to make subviews). If you haven't defined the el attribute in the view, then this.$el will represent a DOM element that hasn't yet been placed. After you render whatever HTML in this.$el, then append or insert it into the DOM, $('body').append(this.$el)

It appears that you can't do any Canvas drawing on this.$el until it's been placed into the document.

Simple, 1 page, Example Backbone.js application in CoffeeScript

Everything you need to get started and understand the mechanics for a Backbone.js blog app

More resources