Search

At the Forge - Backbone.js

JavaScript is changing. Actually, I'm not sure how much of that is true;
the underlying language hasn't changed too much over the years. But,
even if the language itself isn't changing, everything else about it
is. There's a growing interest in server-side JavaScript, with
high-speed applications based on Node.JS (as described in last month's
column). Browser-based JavaScript is not only pretty standard, but
also executes very efficiently. And, of course, a number of
high-quality, open-source JavaScript libraries exist, such as
jQuery, MooTools and Prototype, that make it easy to work with
JavaScript within the browser.

So, have our JavaScript demons been exorcised
forever? Not at all. As
applications migrate to the browser, there is a growing interest in
making them even more desktop-like. Sure, JavaScript UI libraries
make it relatively easy to implement desktop-like functionality,
such as drag and drop. But if you want to create a truly
sophisticated, desktop-like application, you're going to find yourself
lost in a forest of event callbacks, not to mention widgets that might
or might not be appropriate for such an application.

Thus, it shouldn't come as a surprise to find that in the last year or
two, a new type of Web application has emerged—one written almost
purely in JavaScript, which executes inside the browser, and which
only occasionally contacts a server. This turns the usual model of
Web development—in which the majority of the processing takes place
on the server, emitting HTML and JavaScript that handles things until
the next call to the server—on its head, making the server little
more than a storage facility that stores and retrieves information
determined by the browser application.

You could argue that Google Maps, Gmail and Google Docs—to choose
three famous examples, but by no means the only ones—have been
demonstrating such capabilities for several years. But until
recently, it was relatively difficult for average developers to create
applications that were heavily based on JavaScript.

Fortunately, things have changed, and in a big way. If you want to
create a rich JavaScript application, you have a variety of toolkits
from which to choose. The question no longer is whether you can
create such an application, but rather, which tools you will use to
create it and how it'll talk to the server. Just off the top of my
head, I can recall Backbone.js, Knockout, JavaScript MVC, SproutCore,
Closure and Cappuccino, and you can be sure that I'm
mentioning only a small fraction of the toolkits that exist. It might go
without saying nowadays, but I should add that the leading toolkits
are all released under open-source licenses, making it possible to
download, try and explore each of these libraries without having to
worry about licensing restrictions when downloading or deploying them.

This month, I'm starting a series of columns about several of
these in-browser application development frameworks, and how you can
use them to create richer, more interesting Web applications. In each
case, I'll explore how easy it is to get started with the framework,
its relative advantages and disadvantages, and discuss how you might have it
interact with data on a server.

During the past decade, we have seen a clear trend toward MVC frameworks
on the server side that provide RESTful APIs. Ruby on Rails isn't the
only framework that has promoted such a development style, but it
certainly has pushed developers hard in those directions, making non-REST
and non-MVC development frustratingly difficult. It turns out that
many of the new, modern JavaScript frameworks also have adopted the
MVC model, each in its own way and always with differences between
the server-side model that Rails developers might expect.

Using MVC on the browser and on the server (which I like to call
MVC-squared, but maybe that's just me) turns a Web application into
two separate software systems: one on the server that's basically
exposing a RESTful, JSON API to the world, and another in the browser
that's consuming a RESTful, JSON API from a server. Decomposing the
program into these two parts makes it easier to split the development
work across two individuals or groups, as well as to organize the code
in a smarter way. I'll have more to say about this in the coming
months, as I connect JavaScript applications to back-end storage
systems.

This month, I take an initial look at Backbone.js, a very small
JavaScript library that has been getting a fair amount of press
recently. And, I explain how to build a simple application using Backbone.js,
creating functionality that exists completely within the browser.

The Basics

Backbone.js, as I indicated above, follows the model-view-controller
(MVC) paradigm that has been used by software developers for several
decades, and that has become ubiquitous in server-side Web
development during the past few years. An MVC application has
three distinct parts: the model (which provides an interface to the
data itself), the view (which presents information to the user) and
the controller (which directs the user's requests to the right models,
and then renders the results in the view). By dividing the program
logic along these lines, it becomes fairly obvious where each function
should go, making the code more maintainable.

In the MVC world of Backbone.js, the split works in a similar way. You
retrieve and store data in a model object, individual methods (and URL
routes) are defined in a controller object, and the view shows things
in the user's browser.

But, if you're coming from the server-side world, there are some
subtle (and not-so-subtle) differences between server-side and
client-side MVC. For starters, it's pretty clear in a server-side
program that the model retrieves data from a database, either
relational or non-relational. By contrast, the model in a JavaScript
application can come from...well, it can come from a variety of
sources, one of which would be a server-side Web application. I'll
look into this more next month; for my examples this month, let's
assume that the data is already in JavaScript and doesn't need to be
loaded from anywhere.

In a server-side application, the view is actually a combination of
HTML, CSS and JavaScript, rather than being a single file or format.
Actually, the view doesn't have to be HTML; it also can be XML, JSON
or a variety of other formats, from CSV to PDF to images. By
contrast, the view in a Backbone.js application typically is going to
rewrite a single part of the current page, rather than load an
entirely new one.

So with this in mind, let's create a basic Backbone.js application.
I've decided to jump onto the social bandwagon and develop
a tiny application that lets people look at a list of recipe titles,
click on a title that sounds interesting, and then read the contents
of the recipe in question. The same principle could apply to an
address book, a diary or even an unusually formatted blog.

So, let's start with the data model. Creating a data model in Ruby on
Rails (using ActiveRecord) is easy. You define a subclass of
ActiveRecord, thus inheriting all of its capabilities. Of course,
JavaScript doesn't have a traditional object model with classes and
inheritance, so Backbone.js needs to use a different paradigm.
Instead, what you do in Backbone.js is invoke the “extend” function on
Backbone.Model. Attributes passed to Backbone.Model.extend either
are treated as data fields or as methods, depending on whether they're data or
functions. For example, if you want to model a single appointment, you
could do it as follows:

Note that you also could define an “initialize” attribute, which would
take the place of the default constructor method. In this particular
case, I'm not planning to do anything fancy, which means I can
use the default. To create a new appointment, you can say:

Of course, most people have to schedule more than one appointment,
which means that this example program needs to keep track of more than one
at a time. Now, you normally might assume that you simply could store
more than one appointment in a JavaScript array. But, in the world of
Backbone.js, you actually use a special kind of object, known as a
collection, to store appointments.

Why a collection and not simply an array? Mostly because it works
together with other items in Backbone.js. For example, you can set
things such that whenever you add or remove an element to your
collection, it automatically will invoke another method. For another,
collection objects incorporate the Underscore library for JavaScript,
which defines a number of methods from functional programming, such as
map and pluck, so retrieving information from your collection is
quite straightforward.

Just as you defined a model by extending Backbone.Model, you define a
collection by extending Backbone.Collection:

Appointments = Backbone.Collection.extend({
});

Any attributes that you define on the collection are then available, as
data or functions, on collection objects of this type. In this
particular case, I defined two different attributes, the
initialize
constructor and the update_appointment_counter method:

In this case, the constructor uses jQuery to initialize the appointment
length counter (to zero, given that the collection is only now being
initialized) and then adds two handlers to the “add” event. Each
time you add a new appointment to this collection, two different
functions will fire. One of them
(options.view.add_appointment_row)
will add a new row to the HTML table containing a list of
appointments, and the other
(this.update_appointment_counter) updates
the counter. As you can see, the functions can be defined in a
variety of places; it probably would have made more sense to put both
of these methods on the view.

Experienced JavaScript programmers know what “this” is; thus,
this.update_appointment_counter makes sense. But, what is
options.view? Well, it might help to see how you create your
collection, inside the view constructor:

Basically, you're saying that the appointments attribute for the view
is an Appointments collection, starting with no data. Passing a
second parameter allows you to set one or more options in a JavaScript
object, which is then available as “options”. Because the view passes
itself (!) as the “view” option when creating the collection, you
then can
access the view from within the collection as options.view.

The upshot is that your view, thus, has access to your collection (as
this.appointments), and your collection has access to our view (as
options.view). This sort of simple, two-way communication is typical
for Backbone.js, which tries to make things as simple and short as
possible.

The code doesn't include a controller. That's because controllers are
necessary only if you want to provide a number of different URLs—well,
fragments at the end of a URL—that invoke different methods.
For now, you can do without it, but a larger application certainly
will require it.

Views

As always in the MVC paradigm, the view is where things are displayed
to (and interact with) the end user. In the Rails world, a view is
almost always rendered by the controller; your application doesn't
need to create it explicitly. In the Backbone.js world, a view is
just another object that can be created, often by a model, and which
has many controller-like functions. You create it, as you might
expect, with:

AppView = Backbone.View.extend({
});

So, you can think of Backbone.js views as fragments of HTML that are
added to the current page, plus some of the functionality that you
might associate with a controller. Each view is associated with a DOM
element. By default, it's a regular “div” element, but you either
can set it in one place (using the “el” attribute), or you can set it
using a combination of the “tagName”, “className” and
“id”
attributes as well.

As with models and collections, you can use the “initialize”
constructor to set up one or more objects. In the case of this example
application, you'll initialize your Appointments collection without any
element members, as you saw above when I discussed that collection.

You also will define an event handler, such that clicking on the
“add-appointment” button will do so:

In other words, when you click on the “add-appointment” button, the
“click” event handler executes the add_appointment function. This
function grabs the values from the little form and uses those values
to instantiate a new appointment, adding it to the collection of
appointments.

But, you also have event handlers running on the collection! The first
handler updates the appointment counter, and the second adds a new row
to the table of appointments. It adds the row by cheating a little
bit. Although it would have been more elegant to have a second view with
an element of “tr” that would add a new row, I decided to mimic some
of the on-line tutorials I've seen, adding a new row in a slightly
simpler way—namely, an ugly text string.

If I weren't interested in creating an entirely new view, I could
have used the “template” function that Backbone.js inherits from
underscore.js, giving me ERb-like templates that can be filled in more
nicely. Something else that I could have done is break this
application into smaller pieces. Although it's nice to have everything in
a single file when working on something small, a larger Backbone.js
application could well be put into multiple files, with each file
defining a different object. Developers experienced with any modern
server-side MVC framework, such as Rails or Django, will understand
the advantages of putting things into separate files.

Backbone.js is one of the smallest and easiest-to-understand MVC
frameworks for JavaScript applications. It has become quite popular,
as evidenced by the number of blog posts about it in the past few
months. The support that its authors, Jeremy Ashkenas and others at
DocumentCloud, have offered to many Backbone.js users has been quite
impressive to see as well.

Although this column obviously didn't go into great depth about
Backbone.js, one shortcoming in this application should have been
obvious. What happens when the user wants to store data? Right now,
the appointment calendar is not only simple-minded in its interface
and execution (for example, there's no way to look at just today's
appointments, let alone remove or edit existing ones), but it also
fails to provide persistent storage.

Next month, I'll discuss how you can connect a Backbone.js application to
a persistent back-end database or server-side MVC application (thus
providing an MVC-squared solution), giving users and developers the
best of both worlds—flexible development with dynamic JavaScript, but
with a robust back end that can persist data easily.

I encourage anyone interested in Backbone.js to read through the code
and comments. I certainly learned some things about Backbone.js in
particular and JavaScript in general from reading through this code.

Reuven M. Lerner is a longtime Web developer, architect and trainer.
He is a PhD candidate in learning sciences at Northwestern University,
researching the design and analysis of collaborative on-line
communities. Reuven lives with his wife and three children in
Modi'in, Israel.