As the front end of applications becomes increasingly complex, client MVC JavaScript frameworks can help you to tame Ajax problems. There are numerous frameworks, and it can be hard to know where to start. In this article, get a brief overview of some of the most popular frameworks: Backbone.js, Spine.js, Knockout.js, and Batman.js. Learn how to make the right framework choice for your next project.

Share:

Peter Bell (peter@pbell.com), Senior VP of Engineering, General Assembly

Peter BellPeter Bell is Senior VP of Engineering and a senior fellow at General Assembly, a campus for technology, design, and entrepreneurship. He presents internationally and writes extensively on JavaScript, domain-specific languages, agile architecture, NoSQL, and requirements and estimating. He has presented at a range of conferences, including DLD Conference, ooPSLA, RubyNation, Code Generation, the British Computer Society Software Practices Advancement conference, and the Rich Web Experience. Peter has been published in IEEE Software, Dr. Dobbs, IBM developerWorks, Information Week, and GroovyMag.



12 June 2012

Also available in Russian Japanese

Introduction

Fifteen years ago, many of us were building websites using tools like Perl and ColdFusion. We would often write scripts that queried the database at the top of the page, apply the necessary transforms to the data, and display the data at the bottom of the same script. Such an architecture worked fine for adding a simple "Contact us" form to a website. However, as applications became more complex, that approach didn't scale to handle the complexity. Most web applications have now standardized on a Model-View-Controller (MVC) architecture with separate code responsible for the business logic, the display logic, and the user interaction (routing) logic. A plethora of frameworks, from Spring MVC to Rails, has emerged to help you quickly implement MVC-based web applications.

Frequently used abbreviations

  • JSON: JavaScript Object Notation
  • MVC: Model-View-Controller
  • REST: Representational State Transfer

A few years ago, jQuery was becoming the dominant library for building client JavaScript applications. With increasingly complex JavaScript in our applications, however, jQuery became necessary but insufficient for handling the complexity. For example, a single-page application for a to-do list could have a list of urgent to-dos, a complete list of to-dos, a list of to-dos for today, and a list of overdue to-dos. What happens when you delete a to-do? If the task is urgent and overdue, you might have to manually write code to remove it from three or four different places in the view. If deleting an object then requires you to delete or change other associated objects displayed on the screen, the complexity can get unmanageable.

Client MVC frameworks are designed to solve such problems, and most of them do a good job. But how do you decide which framework to use? Many JavaScript client-side MVC frameworks are available. This article provides a high-level overview of some of the most popular frameworks. Learn how to select the right framework for a given use case.


Backbone.js

In terms of adoption, Backbone is by far the most popular client MVC framework. It's widely used by many communities; there has been notable adoption by Rails developers, with popular resources like Backbone on Rails by thoughtbot—a well-respected Rails consulting shop (see Resources). The strength of Backbone.js is its slick integration with Representational State Transfer (REST)-ful web services. If you're using a RESTful JavaScript Object Notation (JSON) model for your back-end data and follow the conventions that Backbone expects (which match the conventions used in Rails), you don't need to write any code to wire up Backbone to your server. This could save a great deal of time.

In Backbone, an application comprises collections (users or articles), models (a single user or article), views, and routers. The views in Backbone.js are nonprescriptive, allowing you to use your favorite JavaScript templating or rendering framework. Routers, a combination of Rail-style routers and a traditional MVC controller, are responsible for taking a given URL and telling the framework what code to run. The Backbone.js router code in Listing 1 shows an example.

Listing 1. Sample Backbone.js router code
var Workspace = Backbone.Router.extend({

  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/kiwis
    "search/:query/p:page": "search"   // #search/kiwis/p7
  },

  help: function() {
    ...
  },

  search: function(query, page) {
    ...
  }
});

Backbone.js comes with a copy of Underscore.js. Underscore.js is a set of utilities that makes it easier to write JavaScript in a more functional style and supports a range of useful collection-based operations. It also includes Backbone.history, which helps you elegantly handle page navigation.

The primary advantage of Backbone.js is its automated integration with the server. If that's a good fit for your use case, it might be worth the learning curve to work with Backbone.js. You can get started in an hour or two with some of the frameworks. With Backbone.js, you'll probably spend at least a day or two learning the basics. It is a good fit for larger projects that you'll be working on for at least a few weeks.

Backbone.js is still not a complete solution. You'll likely have to write a fair bit of code to handle issues such as potential memory leaks. You might also have to experiment with several approaches to view rendering before you get something that fits your needs.


Spine.js

Spine.js is often compared to Backbone.js; it was influenced by Backbone.js and is close to it in terms of adoption. Spine.js has classes, models, controllers, and views, which are a bit more traditional than the collections that Backbone.js introduces.

Spine.js is written in CoffeeScript (see Resources), which makes it more concise and (in my opinion) easier to read the source. To understand how Spine.js works, you need to familiarize yourself with CoffeeScript. You, however, don't need to build your Spine.js applications in CoffeeScript. If you do build in CoffeeScript, though, you can access CoffeeScript features, such as classes. JavaScript uses prototypical rather than classical inheritance, so there is no support for classes natively in JavaScript. CoffeeScript uses some fairly standard patterns to provide classes for developers who want to use them. If you're writing your Spine.js applications in plain JavaScript, you can simply use Spine.Class: var Users = Spine.Class.sub();, which provides access to classes without having to write your code in CoffeeScript.

The models, controllers, and views in Spine.js are implemented using classes so you can write both class and instance methods. Models are responsible for business logic and are modules; you can extend and include other modules for mix-in-style reuse of properties and functions. Models can be automatically serialized to JSON to be persisted by simply using local storage. Or, you can handle persisting objects to a server using Asynchronous JavaScript + XML (Ajax). Like Backbone.js, Spine.js now provides sensible defaults for persisting using Ajax, although it is easy to write your own custom implementation if necessary. Listing 2 shows an example of CoffeeScript code from a Spine.js application.

Listing 2. CoffeeScript from a Spine.js application
class Contact extends Spine.Model
  @configure "Contact", "first_name", "last_name"

  @filter: (query) -> 
    @select (c) -> 
      c.first_name.indexOf(query) is not -1

  fullName: -> [@first_name, @last_name].join(' ')

The most important distinction between Spine.js and Backbone.js is how they handle server interactions. Backbone.js waits for the server before displaying a response. If you try to delete, insert, or update an element, the user interface (UI) won't refresh until the operation has been successfully completed. With Spine.js, the focus is on immediate updates to the UI, and the server Ajax is handled as background processing. This update is a significant practical and philosophical distinction to consider when choosing between these two refined, popular, and well-documented frameworks.

If you're primarily creating a client-side experience, where updates to the server state are secondary, Spine.js might be a better choice. If you're still primarily using the server to check the validity of state changes, Backbone.js might be a closer fit. Spine.js provides a more responsive UI. But what happens if it shows the successful deletion of an element, only to have the server send a response that you are not allowed to delete that item because it's in use by someone else? There are workarounds, but generally Spine.js is a better fit for use cases where users are operating on their own—not shared—data. For example, a common use cases for Spine.js is a shopping cart, where all of the validation constraints can be handled on the client side.


Knockout

It's possible to argue whether any of the tools discussed so far are true MVC frameworks in the original sense. Knockout is clearly implementing the Model-View-View Model (MVVM) rather than classical MVC. Don't let that get in the way of your decision-making process, though. In selecting a framework, it's much more important to look at the functions provided rather than the acronym or classification.

Knockout.js is especially popular with Microsoft .NET developers who are familiar with the MVVM model. It is a great choice for a wide range of use cases where declarative binding of the model state to the view is the primary problem to be solved. Knockout.js could be a really good fit for the previously mentioned example to-do application, with several different views of a different subset of the same master to-do list, all of which need to be updated when a to-do is deleted.

In Knockout.js, you create models, view models, and views. The models, as in Spine.js and Backbone.js, are responsible for the business logic, validations, and the Ajax to interact with a remote server—assuming you're not just creating a local application. The view models are the code responsible for holding and operating on model data. For example, a view model might contain methods for adding, editing, and removing items from a list. A view model is most closely related to the controller in traditional MVC architecture. The views are the templates that contain the markup for rendering information to the screen. In Knockout.js, these can be declaratively bound to the view models (making it easy to get started). Some students can be up and running with Knockout in about an hour, and building non-trivial applications in a three-hour workshop.

Generally, Knockout.js is good for smaller, simpler projects. People tend to pick Backbone.js or Spine.js for larger and more complex projects. That said, experienced Knockout.js developers can create sophisticated applications that are still easy to maintain. If you're considering Knockout.js, you should also consider Angular.js and Sammy.js (see Resources), which are relatively lightweight frameworks with easy start-up.


Batman.js

Batman.js, an interesting new framework, was nominally introduced at JSConf in 2011, though it took a few more months before being available for download. Batman.js has started to gain traction among programmers who like its approach to developing MVC applications. On the surface, Batman is similar to Knockout.js in terms of ease in getting started and support for declarative bindings in the view. Batman.js provides several other capabilities, including an optional full-stack framework for automated code generators, build tools, and even back-end node.js server code for implementing your server-side API.

Like Knockout.js, Batman.js uses view bindings. Listing 3 shows sample view code.

Listing 3. View code example in Batman.js
<ul id="items">
    <li data-foreach-todo="Todo.all" data-mixin="animation">
        <input type="checkbox" data-bind="todo.isDone" data-event-change="todo.save" />
        <label data-bind="todo.body" data-addclass-done="todo.isDone" 
            data-mixin="editable"></label>
        <a data-event-click="todo.destroy">delete</a>
    </li>
    <li><span data-bind="Todo.all.length"></span> 
       <span data-bind="'item' | pluralize Todo.all.length"></span></li>
</ul>

The code in Listing 3 is valid HTML5 with additional attributes that Batman uses to bind data and events to. In Batman.js, your application consists of models, views, and controllers. Models support validations, have the capacity to implement life cycle events, include a built-in identity map, and can be told (active record style) how to persist themselves using Batman.LocalStorage, Batman.RestStorage, Batman.RailsStorage, or your own custom implementation. Views are JavaScript classes that render templates written in pure HTML, with bindings using data-* attributes to bind model data and trigger event handlers. Controllers are persistent objects that handle the events from the views, access the model data, and render the appropriate views.


Selecting a JavaScript framework

If you're undertaking a large, long-term project, it's worth reviewing either Backbone.js or Spine.js because of their widespread adoption and the support available for issues you might encounter. However, even with these projects, understand that you'll still be writing more infrastructure code than would be necessary with a mature server-side MVC framework.

Experimenting with one of the frameworks that uses declarative bindings in the views would be worthwhile. Such frameworks have a different set of strengths and weaknesses from a project like Backbone.js. If you're considering declarative view bindings, take the time to explore the additional capabilities that the newer Batman.js framework provides. It's less popular than other frameworks, but it is growing rapidly in popularity and provides a range of additional features beyond the simple client MVC framework.

It's important to prototype in various frameworks to get a feel for what it's like to work with them. Especially for client MVC frameworks, prototyping is one of the quickest and most effective ways to select from the different options. One approach is to have each team member spend a day or two to prototype using a few different frameworks, then do a retrospective and discuss the results. Worst case, if you still have a couple of contenders, spend another day or so building a proof of concept in both until a clear winner emerges for your use cases.

Plan for flexibility. Think carefully about what you can do to minimize the dependencies on your framework, which can be a difficult task with a lot of the frameworks. Have a backup plan for porting to another framework 12 to 18 months down the road if you find that your needs and the framework that you picked are not moving in the same direction.


Summary

JavaScript client MVC frameworks are still immature. The landscape is changing quickly, and there are few agreed-upon best practices. For larger projects, both Backbone.js and Spine.js are popular and well-supported options. If you prefer declarative view bindings, Knockout.js and Batman.js are both worthy of further investigation.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=819506
ArticleTitle=A survey of client MVC frameworks
publish-date=06122012