Javascript Frameworks And Data Binding

UI Widgets

One approach to frameworks is to try to find the one with the best set of UI widgets that fit your needs. UI widgets have powerful abstractions, but at some point we need to extend them, compose them, or create entirely new widgets, so I am looking for a foundational framework that a UI can be built with from the ground up or used to integrate with UI widgets.

jQuery

jQuery and other similar libraries largely insulate the programmer from browser inconsitencies and add convenience for DOM and event interaction. It is possible to build a UI with only jQuery, but in my experience this is not a productive option because jQuery apropriately concerns itself with only the lowest level of abstractions. JQuery plugins and UI Widgets tend to be very opaque data containers that are tied to the DOM. It can be difficult to communicate with the data they are holding or otherwise extend them for new use cases. jQuery is not an MVC framework, although you can try to use it in that style.

MVC frameworks

MVC frameworks crucially separate out the data from the UI. Data is accesible in the models rather than being retrieved directly from the DOM. This is crucial for an interactive UI because the display of data (always a string) is different than the actual data. When data changes we need to interact with the real data, not a display of it. Traditionally we might have interacted with the data by querying the server for even a minor update. However, a much more responsive application can be built if we have access to the data client side and can optimistically make changes in the UI before ever hearing back an OK from the server.

Backbone.js

The original and very popular javascript MVC framework is Backbone.js. I have used Backbone.js on a few projects now, and I believe it is definitely better to use Backbone than to not use it. The good part of Backbone is that it structures your data into models and collections separate from (but bound to) the UI with good REST integration. However, in my experience the UI integration is not good enough. There is too much boilerplate from its lack of 2-way data binding. In Backbone you have to wire your templates to re-render when data changes. You also have to retrieve your data back out of your forms to put it in your models/collections. This boilerplate data binding is tedious for the experienced, but also ends up being a stumbling block for less advanced programmers that lets them write bad code.

Let me explain the point about less advanced programmers. Normally I am of the mindset that you need to focus on hiring better and teaching your team how to code better. However, in a client heavy interactive app we may have UI focused programmers. They may be capable of creating great UIs but do not always create the best engineering quality code. This could be because they are an experienced CSS designers that just recently learned javascript. But it can also be because they care deeply about the end result, and the code that it takes to get there is not as interesting to them. There are probably good UI programmers that would be great to have on your team, but that you could struggle with to guide them to create high quality code (code that is understandable, low defect, and easy to extend in the future). In my opinion, Backbone asks for too many unecessary decisions and boilerplate code from inexperienced programmers that can easily lead to low quality code.

Knockout / Knockback

Knockout is one of the original frameworks with good 2-way data binding. Knockout is a bit lacking as a feature complete framework. There is a project called Knockback.js that brings in rich models from Backbone.js. However, while Knockback seems feature complete it is a heavyweight in size and there is still some boilerplate to declare powerful 2-way data bindings.

more frameworks with 2-way data binding

The remaining framework options I know of with 2-way data binding are then Angularjs, Ember.js, Batman.js, CanJS, and Serenade.js.

Another intriguing option is Derby.js and Meteor, which tries to bring data-binding all the way to the server side. Derby.js is designed to work with a server-side nodejs server, so although it may be possible to integrate with a non-node setup, I will wait for a clear demonstration of that before trying it out. Batman.js does target running on the server side also, but should work on the client side with other backends.

Event Binding

One difference in frameworks is their approach is event binding. CanJS uses CSS selectors for event binding:

var Todos = can.Control({
    'init' : function(){}
  },
  'li click' : function( li, event ) {
    console.log( 'You clicked', li.text() )
  }
})

This keeps logic out of the view, which has benefits. However, this means you have to make sure your css selector always matches the view html. For that reason, I prefer other approaches to event handling.

Templating

One difference between these frameworks is how they do templating. Angularjs, KnockoutJS, and Batman.js templates are valid html. Templating then involves using existing custom tags, setting tag attributes, and some creation of custom tags. I have personally never been a big fan of tag-based templating. However, for client-heavy templates it begins to make some sense. I used to think of templates as generated and read-only. Assign some values to some variable, stick them into your templates, its easy, why try and pretend we are using html? I still think this makes sense when you are writing a stateless webapp. However, when you begin to bind interactivity to templates you quickly run into new issues.

In Ember.js actions can be bound in the template:

<a href="#" {action "edit" on="click"}>Edit</a>

An Ember View can also listen for events. The problem for me with Ember style views is that the basics need to abandon html to be concise. For example, Emberjs has its own checkbox function

{view Ember.Checkbox checkedBinding="todo.done"}

compared with angularjs (Batman.js and Knockout are similar)

<input type="checkbox" ng-model="todo.done">

The Emberjs version is not verbose. But the html version is easier to understand, especially for a designer because we only added the binding, we did not use any additional abstractions. I would like an ember-style version:

<input type="checkbox" {checked=todo.done}>

However, without knowledge of the tag, Ember-style templates probably need to special case checked to figure out the correct behavior. For server side templating this wouldn not matter: I could do something like this:

<input type="checkbox" {todo.done ? "checked=checked" : ""}>

There is no updating, so we can just make sure to get it right the first time. But for automatic 2-way data binding this will not work unless we have a construct to recognize which part of the expression is a data binding (todo.done), and which part is just for generating the proper html. So things quickly get more complicated unless we make the choice of abandoning html for functions or having our template language have knowledge about the DOM (a benefit of having templates that are valid HTML).

Another approach to templating is to use a thin abstraction over html to make writing HTML nicer. As an example, the Joosy framework defaults to using HAML. As a creator of the Hamlet templating language, I don’t like HAML-inspired syntax. Hamlet gets rid of closing tags and lets you insert values into your HTML. HTML is actually pretty good once you get rid of the closing tags and add a few conveniences. The additional abstractions beyond that makes us learn something new for no reason.

For my javascript templates, I first create them server-side (Yesod or Rails) with Hamlet. In addition to being a more convenient way to write HTML, it also lets me stick data into them server side if I find it more convenient. On top of that, I can still do client-side templating with something like Mustache, and obviously valid html templates of Angularjs/Knockout/Batman.js work very well. Serenade.js uses a css-selector style abstraction for its template languages, so it isn’t forcing you to learn anything new if you are used to CSS, just to use css selectors rather than tags.

2-way Data-binding by example

I am truly impressed with the revolutionary hassle-free data binding of Angularjs. You can use plain-old javascript attributes for bound data instead of get and set. Their boilerplate-free usage of dependency injection means your code will always be easy to test.

To see how pain free the AngularJS data binding is, lets look at some examples of computed properties. A computed property is made up of one or more other bound properties.

We want to have a full name in a view that automatically changes when the first or last name of the person changes:

<span> Person: {person.fullName()}</span>

Ember:

Person = Ember.Object.extend({
   firstName: null,
   lastName:  null,
   fullName: function() { return firstName + ' ' + lastName; 
       return this.get('firstName') + ' ' + this.get('lastName');
     }.property('firstName', 'lastName'); 
});

Batman (Coffeescript):

class Person extends Batman.Object
   firstName: null,
   lastName:  null,
   @accessor 'fullName', ->
     @get('firstName') + ' ' + @get('lastName'); 

Knockout:

  function AppViewModel(first, last) {
    this.firstName = ko.observable(first);
    this.lastName = ko.observable(last);
    this.fullName = ko.computed(function() {
      return this.firstName() + " " + this.lastName();
    }, this);
  }

AngularJS:

function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function() {
  return this.first + ' ' + this.last;
};

Choosing a framework

The downsides of Angularjs is that it is a heavyweight in size and its REST and collection support is more immature than what I am used to from Backbone.js.

If you are loooking for a small-sized framework, and you are not using jQuery already, Serenade.js may fit the bill, weighing in at only 9k with no dependencies. We should note that Serenade.js does not target IE6 support.

If you want to share code with a server side javascript application, Batman.js is designed for that. Knockback.js and Batman.js seem to have good collection/REST support and may be easier options when switching from Backbone.js.

Please understand that this is a very limited comparison. There are probably other frameworks out there with 2-way data binding. Getting data binding right or taking a particular approach to templating doesn’t mean a lot unless there is good documentation on many other well-implemented features and capabilities. I think I have enough experience to know there must be something better than Backbone, but I have little practical experience with the alternatives. One nice way to compare frameworks is the TodoMVC project.

Published: June 11 2012

blog comments powered by Disqus