Earlier this summer I had the opportunity to speak at a fantastic regional conference, Magma Rails.
The location is not what you might expect, being an off-the-beaten-path part of Mexico. There are no crowds of American and European touristas — well none outside of the crowd of non-Mexican rubyists.
On the other hand, you are deep in Mexico, midway along the Pacific coast, with nice beaches and lovely mountains. The sponsors, Mexican rubyists, and people in general are genial and engaging, making us touristas feel right at home.
The original talk topic was going to be Cucumber, instead I opted1 for talk titled Getting started with Backbone and Rails, 25 things you need to know (ShowOff slides). The talk has a fair bit of code, drawn from a working example. The example app was going to be released after the conference…
…and, long story short, that never2 happened…
Over roughly 22 commits, the app is transformed into a one-page Backbone app that:
The app demonstrates how to structure a Backbone app for use with the asset pipeline, how to write views, handle forms, and interact with models, and how to fit Backbone in with Rails.
Let's explore two areas that are cross-cutting to all apps: rendering views and using routers and controllers.
By default, Backbone does not render anything.
Nothing. Zip, zero, zilch. Na–da.
Backbone does create a container element. This very minimalist opinion in Backbone is deliberate. The choice of how to render views into the DOM is entirely up to you.
We are going to render using mustache templates. Not surprisingly, mustache, another minimal yet strongly opinionated piece of software, is a great fit with Backbone.
Let's start by replacing the Rails
index.html view with a
client-side mustache template.
To load our template into the page, we use the asset pipeline's
With the template available on the page, we want to render the template as part
Backbone.View. We create a simple
View class that does little more
than just expose the
render method for now. The
render method executes our
precompiled template and inserts the resulting string as the content of our
@$el. Mustache's render method takes two arguments, the context and a list
of partials. Initially we pass along our movie data as the context
Our movie data is coming from a JSON array inserted into our page by Rails.
Let's transform the data from an array of JSON to a
Backbone directly understands JSON arrays and converts them to instances of
To render our new collection, we introduce a specialized view class:
that will know how to render the individual model instances
We create our single
CollectionView with the collection to render, the
element to render into, the selector for the child view container and a
callback to generate child view instances. Our callback generates a
view (a placeholder at the moment.)
Note: For anything other than a simple string or reference, prefer
configuration via code (callback) instead of configuration via property. For
view option, the less desirable alternative would be to pass a class name
as a property. When in doubt, use a callback instead of a property5. If you
are writing a library, you might consider supporting both.
CollectionView also registers itself for the
reset event. The
event is sent when a collection is updated in bulk.
CollectionView will then
re-render itself and all its child views. The concept of one view managing its
child views (and their child views) is crucial to writing scalable view code.
Our code is ignoring existing views and just creating new views. We will come
back to this when we talk about other events.
Note: Events are the key concept in Backbone. You should know what events
are fired and when by
you need to know that.
At this point we are rendering raw attributes. Looking at
We are copying the model's attributes which are usually just simple types. For example, our opening weekend total is shown as a number instead of currency.
In Rails, we would use a helper like
number_to_currency. Instead of using a
helper6, mustache expects us to give it a formatted currency string as the
opening week total. What we need is a Presenter.
The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view.
Using a presenter allows to expose the model's attributes in a render-ready way without adding formatting logic to the model or the view (commit).
We add a
Presenter class7 that wraps a model's attributes. For example,
our presenter formats our opening weekend total:
And we modify
View to understand presenters by specifying a
We want the collection view to show movies as we add them. And we want the collection view to remove movies as we delete them. When we edit a movie's details, we also want to update the list view. Events make this easy.
Note: Events also mean our code is loosely coupled. We recently tried an experiment with a larger, packaged Backbone framework. Since all our code uses standard events, we were able to wire up a completely new collection view implementation by changing one line and our UI (add, delete, edit, and more) just worked.
Adding a new model to a collection fires the
add event. When we see the
event, our collection view generates a new child view for the newly added
model. Collections will maintain their sort order, so we take advantage of that
to render the new child view in the correct place
The event flow goes like this:
addevent to its listeners
CollectionViewadds a new child view for the new model
Removing a model from a collection fires the 'destroy' event. When we see the
destroy event, our collection view removes the child view for the removed
The event flow goes like this:
destroyevent to its listeners
CollectionViewdestroys its child view
When a view is destroyed, the view should cleanup. Aside from just being good practice, leaving dangling objects around, particularly callbacks, can lead to unexpected and difficult to diagnose behaviors.
Until recently, Backbone had a minimal
remove that only removed the DOM
element (by now you must be seeing the trend in Backbone.) On August 15th,
dispose was added to Backbone master. The
dispose method cleans up
all the events registered via Backbone: the events hash and the model and
collection bindings. Calling
remove will call
dispose for you.
dispose covers the core Backbone cleanup. There is no hook
for non-core Backbone cleanup; for example you might use
trigger events from a widget,
dispose will not know about them.
Instead of calling
remove, we are going to use
destroy which we implement
The view is hidden, we
unbind8, and then we ask Backbone to cleanup.
is the hook to perform our non-core Backbone cleanup. For example,
CollectionView could use
unbind to destroy all of its children when the
parent view is destroyed (and so on if the child views had children).
The next installment will cover routers and controllers and how you should be using them and why Backbone is not MVC, even though it is MVC.
Render your views with mustache.
Use presenters instead of using models directly from views.
Cleanup when you destroy a view.
Be kind to others; be especially kind to children, the elderly, and animals.
1 Started using Backbone in 2010 when it seemed that a minor revison happened every month.
2 Not exactly true, it was used at the inaugural Seattle Backbone meetup on July 10th, 2012.
We are also using
hogan_assets supports hamstache templates
haml gem is available.
Our example app is using an older version of Backbone so we do the work of
dispose in our
unbind implementation. Usually
unbind is matched with
another method for setup.