While it wasn’t as trivial as a traditional server-side implementation in Rails, it did turn out relatively clean. This is a long post, and if you make it through it, let me know what you think.
Our server-side is a stock Rails 3.1 app. We’ll go with a classic blog app consisting of a single
Post model to CRUD.
We’ll use the backbone-on-rails Ruby gem to add backbone.js and underscore.js (backbone.js’s only dependency) to our Rails app.
The backbone.js and underscore.js files are not copied into your Rails app’s directory. They’re both automatically loaded by the
backbone-on-rails gem (the
jquery-rails Ruby includes jquery.js and jquery_ujs.js the same way).
Let’s use the scaffold generator from
backbone-on-rails to generate our client-side Backbone.js objects.
We’ll be using CoffeeScript, the Rails default client-side language, throughout our Backbone app. CoffeeScript’s cleaner, Ruby-like syntax will help make the code more readable and understandable.
We’ll start our app by implementing a “Read” action, the homepage. The homepage will be a list of all our posts. The
backbone-on-rails scaffold generator has already created for us a router, an index view and template, a collection, and a model. To wire up the homepage we’ll need to modify our router.
Backbone routers are like controllers in Rails. They map routes to actions. The empty route represents the default action, in our case, the homepage.
index action, we create a
Posts collection, a view for the collection, and then tell the collection to fetch its posts from the server. We don’t tell the view to render itself because the fetch is asynchronous. We’ll see later how the view uses an event handler to render itself after the fetch.
Posts collection is configured to make server-side requests to our Rails app at “/posts” and to contain
Post model is as simple as it gets. It’s just a
Backbone.Model subclass and contains no custom logic.
Before we take a look at the
PostsIndex view, let’s first quickly go over the Rails layout used on the server-side.
The only change we’ve made to this default Rails layout is the addition of a single “#app”
<div> will act as the container for our app. Each Backbone view will replace its contents.
Now we can take a look at the Backbone view.
On initialization, the view binds its
render method as a handler to its collection’s
reset event (this is the
Posts collection we created in the
Blog.Routers.Posts#index action). The
Posts collection will trigger a
reset event after successfully fetching its posts from the server. When the event is fired, the view will render its template and create and render another view for each
Post in its collection.
Here’s the template used by the
This table will be populated by the view used for each post in the collection:
And its template:
There’s one last thing we need to do before we can view our homepage in a browser: we have to bootstrap our app’s router.
onDOMReady event handler invokes an
init function that creates a
PostsRouter and tells Backbone to start monitoring all hashchange events. All our client-side pages will use hash fragments as URLs. Backbone will listen for hash fragment changes and route them using the routes specified in our
Finally we can now start our Rails server and hit our app at http://localhost:3000/posts. Unfortunately, we don’t have any posts yet. So let’s move on to creating a post.
Creating a Post
At the bottom of our existing home template is a link to create a new post:
This means we need to add another route and action to our
new action, we create a
Post model, then create a
Posts collection and bind an anonymous handler to its
add event. A collection will fire an
add event whenever a new model is added to it. Our
add event handler performs the equivalent of a server-side redirect by navigating the router to the “empty” router (the homepage). Finally we create and render a view to submit a new post.
The element (
PostsNew#el) used in our new post view is the global “#app”
<div>. The new post view will replace the contents of this
<div> with its own template containing a form to submit a new post.
The view also registers a handler to the submit event from the
<form> in its template. This handler stops the default form submission behavior and then asks its posts collection to create a new post. This will result in an HTTP POST request to the server and, if successful, the collection will fire an
add event. This is the same event our
PostsRouter, in its
new action, bound an anonymous “redirect” handler to.
And here is the new post view’s template:
Great, we can now create posts. However, we can’t let people create posts without a title and a body. Let’s see what we can do to validate our new posts.
Validating a Post
Adding some client-side validation will help improve the responsiveness of our UI. We’ll still validate posts on the server-side, but for the user’s sake, I feel this duplication is worth the tradeoff.
Backbone models include a
validate method that can be used to perform custom validations. However, as Rails developers we’re used to
ActiveRecord‘s high-level, declarative approach to validation. And thanks to the jQuery validation plugin, we can have declarative validation on the client-side too.
First, download the plugin (after downloading and unzipping it, remember to restart your Rails server).
Now we can add some validation to our new posts.
The jQuery validate plugin allows you to specify per-attribute validation rules. Here we’re requiring a title and a body. The
validate method will stop the form submission when validation fails. This will also prevent the new post view’s form submission handler,
#create, from executing as well.
Go and give it a try in the browser. Submitting a post without a title and body will now fail and display an error message(s).
Viewing a Post
Ok, so far we can display a list of posts, create a post, and validate a post. Next up is: viewing an individual post.
On our home page, each post row included the following link for viewing the post:
Let’s add a route and action to our router for this post page (i.e.,
This newest route includes a named parameter,
:id. Backbone will automatically pass the named parameter’s value as an argument to our
Currently in Backbone, fetching an individual model isn’t very elegant. Above is one of the cleanest ways. You first create a model with the requested id, then add the model to a collection, and finally, tell the model to
#show action also creates an instance of the following
PostsShow view for the individual post.
During initialization, the view adds a handler to its
change event. The
Post will fire a
change event after it has been successfully
The element (
PostsShow#el) for this view is the same global “#app”
<div> container that was used by the post list and new post views. And like those two views, an individual post view will also replace the contents of this
And here’s the posts show view’s template:
The “back” link goes back to our homepage.
Backbone.js brings the power and maintainability of MVC to the client-side. Routers can be used like Rails server-side controllers to create responsive, single-page apps. Views are also like controllers, but instead of responding to url changes, they respond to DOM level events, such as link clicks and form submissions. Templates contain the app’s markup, they’re what Rails calls views. Models and collections round out Backbone.js, providing traditional class-based, client-side domain modeling.