What the hell am I talking about? Backbone? Knockout?
These are 2 popular javascript frameworks, supposedly designed to help/facilitate the building of rich client application on the web.
To me, this means web apps with non-trivial interactive UI, which would be difficult to manage with just jQuery.
What’s wrong with jQuery, you say? Well, jQuery is awesome, but it’s too low-level as far as app-development is concerned. How are you going to manage it when the complexity increases? It gets hairy very quickly.
If you’ve been developing mostly in jQuery, and you’re getting a bit tired of it, you should be looking for alternatives; higher level frameworks/libraries to help you manage the complexity. However, you may find the information on the internet to be very confusing. What should you use? Which framework/library is better? You might have heard of Backbone.js or Knockout.js, and you don’t know which one you should go with. And to make things worse, Backbone.js and Knockout.js are not the only games in town; there are plenty others as well.
So, I’m going to spare you some of the trouble by telling you straight away which one is better: Knockout.js or Backbone.js
Some people will disagree, but they are ugly and stupid.
Knockout.js is awesome
Backbone.js is pointless
Backbone.js doesn’t solve any problem I’ve ever had. When I look at what Backbone provides, I can’t help but wonder: what’s the point? None of it made me think “Ooooh I wish I had that a long time ago!!”. No, quite the opposite, most of the time I was thinking “Why would I need this?” and “How would this help me?”.
As far as I’m concerned, the only problem that Backbone.js solves is the buzzword compliance problem. “Building a complex client app? You should use MVC”. Good for you; you may now say on your resume “I have experience with MVC”.
I even asked this question on StackOverflow, and the answer was “it helps do MVC”.
Knockout.js actually solves a real problem; a real pain point for anyone who’s ever done front-end work. When you learn how to use Knockout.js … when you go through the tutorials, you’ll constantly feel like “Wow man, this is awesome! Why didn’t I have this before?”
Knockout.js (hereafter referred to as KO) automates what amounts to 70% of Javascript development work, which is listening to events and binding callbacks so that you can do things like:
Update variables/data when user changes form values
Hide or show elements of the page when something happens (or: add/remove elements, instead of simply hiding/showing them)
Change the entire “page” to a different component (a special case of the previous point)
Displaying “items” and “objects” as a complex HTML structure (aka widgets).
Displaying (and updating the display of) a list of items/objects.
KO automates all that manual work. It’s a high level framework so you almost never have to deal with “events” directly; it’s abstracted away so you don’t have to think about the low level aspects of it.
KO also does a great job of really binding the data to the UI. You almost never have to assign a special “class” or “id” for the purpose of manipulating elements and their content from Javascript, as often happens in jQuery-based development.
Tiny disclaimer
To be slightly fair, Knockout.js and Backbone.js have very different purposes.
Backbone.js is not really pointless; it serves as a pretty good ORM for REST APIs.
However, in this article I’m evaluating its usefulness for building interactive UIs on the web.
You may say that this is not really what it’s designed for, but in practice, that’s what most people think Backbone.js is about: helping you build single-page interactive web applications.
That’s what most people end up using it for.
A Closer Look
So far I’ve just been trolling basically. Let me backup my opinion by explaining the reasoning behind it.
Backbone.js
Backbone.js (hereafter referred to as BB) provides you with “Models”, “Collections”, and “Views”.
Models are object wrappers that fire events when any field in the object gets changed. For example, you have a user model. In one corner of your application, you can say:
user.set("name", new_name);
And, in another corner, you can say:
user.on("change:name", function() {
// do something here
// yes, this is an event handler function/callback
// example of what typically goes in here:
$el.find(".name").text(user.get("name"))
});
In essence, you are still dealing with events, and writing event handlers, explicitly. And what’s worse; inside the event handler, you’ll be manipulating the DOM using jQuery (or a similar low-level API).
BB has a “views” system, but it’s not really much of a system; it’s more of a convention. Each view has a model and an element (a DOM element). Convention has it that the view should have a render()
function, which uses the model data to render HTML into the element. Convention also states that render()
should return the view.
ViewName = Backbone.View.extend({
// ...
render: function() {
var $el = $(this.el);
// initialize the element's content by some template
$el.html(loadSomeTemplate("by_name"));
// fill various template blocks, or something like that ...
$el.find(".name").html(this.model.get("name"));
return this; // if you forget this, dragons might hunt you
},
// ...
});
It’s typically inside the view definition that you bind to your model’s “change” events, and that’s basically how you build the interactivity in your application.
And, at the end of the day, it all comes down to the same old low level jQuery mess. You end up adding special classes and ids to your DOM elements, so you can access them easily (from your code) for the purpose of manipulating them. You write a lot of code to listen for events and do stuff.
One of the pain points I remember having with BB (I’m sure it’s been experienced by a lot of other people as well) is the management of views within views, aka nested view, or sub-views. Particularly annoying was the management of views that are meant to display a “Collection” (list) of items. The view itself contains a list of subviews, matching the items in the collection. You end up having to manually write the code to update the view each time the collection changes.
Handling updates to lists/collections can be very complicated, and hard to do efficiently, so most people (including me) end up writing a very generic render function that simply wipes out all the previous subviews and builds new ones. This means, if you have a collection of a 100 items, and you add one new item, you will destroy a 100 DOM elements (each which contain some number of child elements), destroy a 100 view objects, clear out all the bindings between these view objects and the collection items (models), create 101 new view elements, creating a DOM element for each of those, and attaching it to the page (body) DOM.
Naturally, it would be much more efficient to simply add 1 new view and append it to the list, and append its DOM element to the list view. But, like I mentioned above, managing all the various ways in which a list can be updated is quite tricky, so we end up with this solution that just “works”. I’m sure plenty of people had to go through the same trouble, over and over. And most likely, no one bothers to tweak the list-view rendering algorithm to make it smarter, because it’s just not worth it.
BB also suffers from many other complications.
In short, when it comes to building rich front-ends on the web, I found BB to not be effective at all.
However, to be fair, (as I’ve already mentioned), BB is very good at being an ORM for REST APIs.
Yes, perhaps I’m evaluating BB against standards which it was not really intended for: managing the complexities involved in building rich interactive UI. But, as I said, that’s what most people actually use it for, and I find that it’s not really useful in that department at all.
Backbone.js does come with a pretty good url router component, but I think of this as more of a “batteries included” kind of thing. There are other libraries that provide similar functionality, for instance, page.js.
Knockout.js
KO provides a sort of declarative language that allows you to bind DOM elements to your data.
On any DOM element, you can say: this elements takes its value from this variable, or, this element is only displayed when some value is “truthy”, and so on.
KO calls these things “bindings”. A “value” binding on a text field keeps the content of the field in sync with some JS variable. A “visible” binding controls the visibility of an element based on the truthiness of some expression. There are other sorts of bindings, and you can define your own, too.
To have the DOM update automatically when data changes, there must be a way to “listen” to these changes, and the solution KO provides you with is “observables”. An observable holds a value and wraps it up in an object so that you can update that value and have the rest of application see that you made a change.
name = ko.observable(initial_value);
// ....
// sometime later
name(new_value) // change the value
// ...
// to read the value:
val = name()
When you update the value of ‘name’, the rest of the application will automatically adjust. If an element is bound to display the content of ‘name’, it will update itself. If an element is bound to be visible only when name is not empty, it will recalculate its visibility .. and so on.
KO handles all the event bindings for you; you never have to think about it.
KO also provides another very powerful concept: “computed” values. A “computed” object wraps a function (in the functional-programming sense; i.e. it should have no side-effects) and automatically tracks its dependencies.
What the hell does that mean? Let’s illustrate with a simple example. Let’s say that “Full Name” is simply firstName + " " + lastName
. If we would write that as a function:
var fullName = function() {
return firstName + " " + lastName;
}
If you were writing low level Javascript, you’d have events that listen to when the user changes the firstName and lastName fields, and then you call this fullName() function, take the result, and display it inside some div, which you probably put an ‘id’ on so you can access: $("#fullName").html(fullName())
With KO’s “computed” concept, you define fullName like this:
var fullName = ko.computed(function() {
return firstName() + " " + lastName();
});
As far as you care, KO magically figures out the dependency graph for this “computed”, and so every time firstName
changes, fullName
also gets updated.
You can then bind the text value of some div to fullName
, and bind the value of some text field to firstName
, and as the user edits the name in the text field, the div holding the full name will automatically be updated, without you having to do anything extra.
And, as I’ve mentioned before, ‘text’ and ‘value’ are only 2 of the basic bindings that KO provides, and you can also define your own bindings.
Naturally, inside the binding’s definition, you will be doing low-level DOM manipulation using jQuery, but once you have that binding written; you can use it everywhere.
KO comes with a built-in ‘foreach’ binding that iterates over a list and displays the items inside it. As you can expect, the first version of this binding probably wasn’t very smart; it probably just re-rendered the entire element every time an item got changed. But, because it’s a built-in binding that’s used by a lot of people, it can be improved, and as far as I can tell, it’s now quite intelligent; it can find precisely which items have changed, and update only the items that need updating. The official docs say:
(…) The binding will efficiently update the UI to match - inserting or removing more copies of the markup, or re-ordering existing DOM elements, without affecting any other DOM elements. This is far faster than regenerating the entire foreach output after each array change.
The only downside with KO is that the binding syntax is somewhat obtrusive.
This is more or less the first example from the tutorials:
<!-- HTML -->
<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>
<p>Full Name: <strong data-bind="text: fullName"></strong></p>
// Javascript
function AppViewModel() {
var self = this;
self.firstName = ko.observable("Bert");
self.lastName = ko.observable("Bertington");
self.fullName = ko.computed(function() {
return self.firstName() + " " + self.lastName();
});
}
rootVM = new AppViewModel();
// Activates knockout.js
ko.applyBindings(rootVM);
Basically, you define bindings on elements using the attribute “data-bind”, in it, you specify a binding and an expression that goes as an input to that binding. We have here “text” bindings, binding the elements to the “firstName”, “lastName”, and “fullName”.
Then we have the “ViewModel”. Its essentially an object with fields. “firstName” is a field in the “root” ViewModel. Since it’s an observable, you can update it, by running this in the console for example rootVM.firstName("Jack")
, and the page will immediately reflect that change. Not only will the “First Name” element update itself, but the “Full Name” will also reflect that change and update itself.
KO very nicely un-entangles your messy jQuery code.
That’s why it’s awesome.
Here’s the example on jsFiddle: http://jsfiddle.net/FEVbz/
I’ve extended it to also include text fields so you can change values in real time.
I strongly encourage you to use the interactive KO tutorial; you will be amazed: http://learn.knockoutjs.com/
Buzzwords
What is that you say? Knockout.js is not MVC? Well, if you care about buzzwords, here’s one for you: MVVM. As far as I’m concerned, MVVM is gibberish, and so is MVC.
Buzzwords don’t matter. Terms like “MVC” are merely a condensed description of a solution to a problem that developers face in real life.
If you give me a framework that doesn’t solve my problem, I don’t care what buzzword you attach to it.
If you give me a framework that solves my problem beautifully and elegantly, then I’m happy with it, even if it doesn’t fill any buzzword compliance requirement.