In this tutorial, I’ll go over the code for CloudEdit, an example Backbone.js app backed with Rails that outlines some basic patterns that I’ve used successfully in my Rails Backbone projects. I’ll start by describing the spec for the app, and then detail how the models, controllers, and views hook up. This tutorial assumes you already have some basic knowledge about Rails — I’ll be focusing mainly on the Backbone.js concepts.
You can grab all the code for the example app in the GitHub repo. You can also play around with a live version of CloudEdit here.
CloudEdit is an extremely simple document editing app. Here are the specs:
- Users should see a list of the latest documents. To edit, the user clicks the document in the list.
- Users should be able to edit documents with a title and body, and should be able to save their edits to the server.
- Users should be able to create new documents.
First, let’s get the directory structure organized. For our Rails project, we have the usual MVC directory structure underneath the
Notice how the MVC directories mirror the Rails MVC directories.
Let’s first go through all the Backbone related code, and then tackle the Rails code (which is much simpler).
We only have one model: the Document. It has a
title and a
body attribute. But take note that you don’t actually need to specify that in the Backbone model: they’re populated by JSON data, either from the server or from the client.
The only method defined is the
url method, which tells Backbone the URL to persist the
Document model when calling
destroy(). In this case, it’s a pretty typical Rails RESTful model: if it’s a new unsaved model, then it should POST to
/documents for a CREATE action, and if it’s not new, then it should POST to
/documents/id for an UPDATE action.
There’s 3 actions in our app:
- Index: Show a list of documents.
- Edit: Shows a specific document ready to be edited and saved to the server.
- New: Shows a blank document ready to be created and saved to the server.
Note that these match up with the RESTful pages we would expect from a Document resource. You certainly don’t have to organize it this way, but I find that it really helps to have your Backbone structure follow a RESTful pattern.
There’s a lot going on here, so let’s tackle it piece by piece.
I like to organize my Backbone controller and views under
App.Views, respectfully. This helps to disambiguate all the object names, especially at the time of instantiation. Thus, our documents controller is
/#documents/3, it will show the edit page for the document with id 3.
The rule that I use for controller methods is that they should only do 3 things: (1) get the data from the server to populate the models, (2) process that data for the views, and (3) instantiate the views.
For example, in our
index method, we make an ajax JSON call to the Documents#index action on the server to get a list of documents. Then, we iterate through the list and instantiate a Document model for each of the JSON data. Finally, we instantiate the App.Views.Index view with the array of models.
edit method is equally simple: it instantiates the Document with the given id, and then fetches the data for it from the server. Upon success, we instantiate the
Just like with Rails controllers, it is best to keep them skinny. You should delegate most of the complications to the views and models.
The Main App Object
Now that we have the controller setup, let’s actually get our App up and running. The root route for the Rails server will serve up the following HTML, which our Backbone app will use as its scaffolding:
The #notice and #app div elements will be used by the app as a scaffold for the UI.
The main App object itself is dead simple:
The two main things to take note is the instantiation of the Documents controller, and the call to the
Backbone.history.start(). These combined will kick off the necessary listeners to make the hash routing work.
Now that we have the plumbing done, let’s work on the views, which is the actual user facing interface.
For the purposes of this tutorial, I’ll use simple string concatenation in my views. Of course, for more complicated apps, I would highly suggest using a templating framework like jQuery templates or jQote.
This view accepts a list of documents, and simply lists them out with links to edit each of the documents. I want to highlight a few things:
- Backbone automatically (and conveniently) captures the hash passed to the initialization of the object, and sticks it into
- The only method really required by the views is
render, and you can do anything you want to render the view. In this case, all we do is some string concatenation, and stick it into the
- I like to call
renderimmediately at the end of
initialize. What this means is that as soon as you instantiate the view object, it gets rendered.
- Note that there is no event delegation specified. The links to each document is handled automatically by the controller by simply specifying the right hash url to the Edit action. Backbone’s History object automatically handles routing the clicks to the Edit action. This greatly simplifies views which usually have a million events flying around just to do routing correctly.
I decided to collapse both the Edit and New views into one view: the Edit view. Basically, when a Document is passed in that is newly instantiated (and hasn’t been persisted to the server), it acts like a New view, otherwise, it’s an Edit view.
The instantiate-and-render pattern is similar to that of the Index view. The new thing that we see here is the event delegation model. Here’s how it works:
If you specify an event key on the view object, Backbone will automatically delegate the events. The key to each event is in the form “event selector”. In our example, Backbone automatically binds the
save method to the form’s
submit event. This is a very handy shortcut that allows you to organize and peruse events in view easily.
The final thing I want to point out is the
save method, which really bears to fruit the power of Backbone. Unlike normal
save routines, you don’t have to jump through hoops to remember how to persist your data. All of that is already specified in your model. So, all we do is call
save on the Document model (with the parameters taken from the title and body form elements), and we’re done!
The great thing about using Backbone is that it makes your server side code very clean. The main meat of it is simply controllers that pass back JSON data to the client via XHR.
The Document model is simple, and only contains a
title attribute (along with timestamps). One thing to note is that I specifically set
title to be the only attributes that can be mass assigned, and also specifically set which attributes should be returned when calling
to_json. Both of these are for security reasons (you don’t want to client to be able to set or receive attributes they don’t need).
As you see, the Rails controller is dead simple: 4 RESTful actions that return data via JSON. No views and no instance variables. This is RESTful design at its finest!
Conclusion and Part 2
Backbone.js really introduces a new kind of data flow for Rails apps. Instead of data flowing like this:
Rails Model => Rails Controller => Rails View
It now flows like this:
Rails Model => Rails Controller => Backbone Model => Backbone Controller => Backbone View
Read on with Part 2 of the Backbone.js and Rails Tutorial, where we’ll work with Backbone Collections add Underscore templates. and Part 3 where I show how to convert CloudEdit to use Parse and get rid of the Rails code entirely.