At the Forge - Backbone.js

Write simple MVC applications in JavaScript with 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:

Appointment = Backbone.Model.extend({
    person: null,
    meeting_at: null,
    note: null

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:

var new_appointment =
new Appointment({person: 'Barak Obama',
         meeting_at: '2011-jul-14',
         note: 'Meet with the president'});

You also can replace individual attributes within an appointment:

new_appointment.set({person: 'Joe Biden'});

Or, you can retrieve an attribute from an appointment: