KnockoutJS: An Introduction

Glenn Goodrich
Ruby Editor

The rise of sophisticated web applications, such as GMail, has given birth to an army of javascript frameworks. These frameworks allow you to, basically, create a client-side application within your web application. Terms such as “MVC” and other design patterns, once saved for only the server-side of the web, are now applied to just the the client bits of your code. The more popular frameworks these days are http://backbonejs.org, http://emberjs.com, and http://knockoutjs.com. This article will take a whirlwind look at the latter.

(NOTE: It has taken all of my willpower not to entitle this article ‘Momma Said KnockoutJS You Out!’ or something similar. In fact, my willpower needed the help of narcotics.)

Knockout takes the Model-View-View-Model (MVVM) approach to application architecture. MVVM is a architectural pattern that has (some of) its roots in Microsoft and their Windows Presentation Foundation/Silverlight/Failed Attempt at making ASP.NET Web Forms usable. Being honest, I avoided Knockout due to these roots, and I am guessing that many developers do as well. It wasn’t until I inherited an application that uses Knockout that I was forced to I actually learn about it.

Yet again, I have been pleasantly surprised to know that my groundless developer bias was very wrong.

At Least it’s Not MVC

Looking at the Knockout home page, the key concepts it lists are:

  • Declaritive Bindings
  • Automatic UI Refresh
  • Dependence Tracking
  • Templating

I am not going to run through each of these, as you have the same access to their site. I will say what I think it means, in a nutshell. As I see it, the strength of Knockout is it’s data-binding. If you’ve never used a framework that binds the UI to a model (the view model, it this case) the core concept is that any changes to that model are immediately reflected in the view/UI. Adobe Flex is, in my opinion, the most well-known data binding framework. Data binding is enormously powerful, especially if you have pieces of your model strewn about the UI. Updating a value in one place and having 10 UI elements change as a result will bring a smile to any trollface.

Data-binding is the sweetest sauce that Knockout provides, but it is more than that. The templating is very powerful, and you can use any javascript templating library you like (jQuery Templates, mustache, etc). Knockout is also extensible, providing a plugin framework that we will take advantage of in the demo application I’ve put together to show Knockout in action.

Lick it, Lick it Good

The demo application is a simple, yet kind of disgusting, app called LinkLicker. The idea is that links are submitted and you lick the ones you like. Yes, I am a 4 year-old.

The server-side of this application is both a Rails app and irrelevant. Your server side application simply needs to spit back JSON, so it can be Sinatra or Rails or Cuba or whatever. I shant bore you with the details of the web app, but they are in the github repository if you simply must know.

The app shows a form that allows links to be submitted, a list of submitted links, and a list of links that have been licked by the current user. When a link has been licked, it’s icon changes. This, I am sure, is the next FacebookTwitter.

Setup

You will need the Knockout code available in your web app to make this work. I’ve put mine in the vendor directory, along with the most commonly used Knockout plugin. This plugin is the Knockout Mapping plugin, and it takes all the hard work out of mapping JSON from the server to javascript objects on the client.  Note: I won’t really cover the Mapping plugin in this article, but the important bit is it creates observables out of all our JSON properties when it maps the objects. You’ll see what observables are shortly.

Structurally, I like to put each “model” class in it’s own file on the client. LinkLicker really only has one model: Link. I also like to have a global class that represents my application that I call, unremarkably, App. The final directory sturcture for the javascript looks like:

UI

The UI for LinkLicker was driven heavily by my desire to do as little as possible while still getting the Knockout concepts across. As I mentioned above, there is a form, as well as three lists of links. The three lists are All Links, New (meaning, unlicked), and licked links. As you may have surmised, a link can be in more than one of these lists at any given time. The screenshot of the UI is below:

Bootstrap

Bootstrapping LickLinker consists of binding to the form and creating our three lists. We’ll make all of these properties on our App. Easy stuff.

var App = function() {
  this.newLink = ko.observable(new Link());
  this.mylinks = ko.observableArray();
  this.lickedLinks = ko.observableArray();
  this.allLinks = ko.observableArray();
};

You are likely wondering what all this observable madness be. When you make a property an observable, it becomes a super-powerful notifier of changes to that property. Knockout will keep track (as best it can, and it’s pretty good at it) of things that are interested in your property and notify then when it changes. The Observables docs on the Knockout site are great, and there are interactive tutorials to use as well.

In short, and if you can think ahead, when we bind our UI (form) to the newLink it will then notify our form of changes and vice-versa.

The other three properties are observableArrays. These are, conceptually, exactly the same as observables, except that they work on a collection. An observableArray will track what objects are in the array, and it has many helper methods to add and remove these objects. You can easily pop or push items onto an observableArray, slice it, get its length, reverse it, and many other methods you’d expect.

Again, since observables and observableArrays are notifiers of change, when we bind the lists in our UI to these lists of links, everything will be updated to reflect the latest state. Hopefully, this concept is sinking in.

The Model

The model for LinkLicker is just a link and it has 3 properties: url, description, and lickedByCurrentUser.

The Form

Knockout is all about data-binding, remember? As such, we can bind a new Link to the form and, then, as the user enters data it will populate this newly bound Link. In the code snippet above, we create our newLink property as an observable. Now, to bind it to the form:

<form id="create_form" class="one-third column alpha" data-bind="submit: createNewLink"><!-- ko with: newLink -->

 <label for="link_url">Link:</label>
 <input id="link_url" type="text" name="link[url]" data-bind="value: url" />

 <label for="description">Description:</label>
 <input id="link_description" type="text" name="link[description]" data-bind="value: description" />

 <!-- /ko -->
 <input type="submit" value="Add" /></form>

In the above snippet, focus on the <!-- ko with: newLink --> line. Knockout offers a couple of different ways to bind parts of the DOM to the view model. This example uses “containerless bindings”, which means, it doesn’t create a DOM element for that declaration.

The other way to do it is to create a DOM element (such as a div) and add a data-bind attribute specifying the template. There are loads of examples on the Knockout site of the latter if you are confused.

That ko with: newLink tells Knockout to find the newLink property of whatever this view is bound to and scope everything within the block to it. This should be a bit clearer if you look at the input tags. Each input tag has a data-bind attribute that binds the value of that input to a property on newLink.

The Ties the Bind

With our UI in place, we need some mechanism to bind it to our view model. In this app, the view model is our global App class on the client. The App is bound to the view when the page is loaded, as shown here:

$(function(){
  App.globalApp = new App();
  ko.applyBindings(App.globalApp);

  App.globalApp.loadLinks();
});

Easy peasy.

Filtering a list can be done by creating two “computeds” (formerly dependentObservables). Here, let me show you:

this.allLinks = ko.observableArray();

this.lickedLinks = ko.computed(function() {
  return this.allLinks().filter(function(link) {
    return link.isLicked();
  });
}, this);

this.newLinks = ko.computed(function() {
  return this.allLinks().filter(function(link) {
    return !link.isLicked();
  });
}, this);

A computed is an observable that depends on the values of other observables. You can see that we filter the allLinks observableArray based on the value of isLicked() for each link. The second argument to the computed (this) is the context to bind the function.

Computeds are enormously helpful and you’ll find yourself using them everywhere.

Extending KnockoutJS

While Knockout has many, many bindings that you can use, such as text, html, css, visible, you will find occasions that require a new binding. Writing a custom binding is very simple. As an example, I am going to write a hidden binding which is the opposite of the visible binding. It’s is below:

ko.bindingHandlers.hidden = {
  update: function(element, valueAccessor){
    var value = ko.utils.unwrapObservable(valueAccessor());

    ko.bindingHandlers.visible.update(element, function() {
      return !value;
    });

  }
};

In the binding, I grab the value of the element being bound, and then pass it to the existing visible binding. The result is negated and returned.

Obviously, this is a trivial binding, but you can get very complex if needed. Google will show you bindings for jQuery UI elements, for example, along with thousands of other custom bindings that the community has authored.

Momma Said Wrap This Up!

There were many other aspects of Knockout I would like to cover, but I think we’ve taken a nice look across many of it’s features. Hopefully, when you are evaluating javascript frameworks, this article will help you make a decision.

Remember, the entirety of the LinkLicker source is yours to browse, use, mock, whatever on github.

If you have questions on LinkLicker or Knockout, hit me in the comments. Thanks for reading!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.