David East, but the top half of the face

David East

Web Developer / Designer

Knockout for First Timers

May 24, 2012

Writing complex applications with jQuery alone can be extremely cumbersome. This problem has been remedied by several JavaScript frameworks. However, there are so many frameworks that it’s a chore in itself to decide which one is the right tool for the job.

Today, we’ll learn what sets Knockout apart from the rest. If you want to learn a framework that is painlessly easy to use, and still provides a myraid of useful features, then this tutorial is for you. So what are you waiting for? Let’s dig in!

Why Knockout?

The beauty of Knockout is that it is simple to understand. Many other JavaScript frameworks are robust and featured filled, but their learning curve can be steep. Knockout still provides many features, but it simplifies writing complex user interfaces. The list below details some of these features:

  • The MVVM (Model-View-ViewModel) pattern separates out application logic and responsibility.
  • Declarative bindings allow for easy but yet powerful ways to map properties to your DOM elements.
  • Control flow bindings free you from clunky and confusing script templates.
  • Observables help provide for an extremely responsive UI.
  • Plenty of documentation and community support.

Download

The download for Knockout can be found on the official website’s download page. If you prefer a CDN, CDNJS.com has it hosted as well.

We’ll get started with discussing the view model.

The View Model

The view model is the model for your UI. Within your view model you can define properties that will display on your view.

Below we are creating a view model that contains two properties. One property, shakes, is a an array of shake types. The other property, customerName, is simply the name of the customer.

Every view model is kicked off with Knockout’s applyBindings function that takes a intiailized view model.

// We create the view model using the constructor function for easy initialization later
function ShakeShopViewModel() {
  this.shakes = ['Vanilla', 'Chocolate', 'Harlem'];
  this.customerName = 'John Elway'; // default to John Elway
}

// ko is the instance of knockout, like $ for jQuery
ko.applyBindings(new ShakeShopViewModel());

The Underlying Data Model

You will often hear the view model referred to as the underlying data model. Think of the view model as a layer underneath your view where you can access all of the properties you have defined. How do you access these properties from the view? Easily, with declarative binding.

Declarative Binding

With declarative binding you can map your view model properties to DOM elements in your view. Declarative binding is simple.

<span data-bind="text: customerName"></span>

Declarative binding uses the HTML5 data- attribute. The data-bind attribute takes in a specific binding, in this case the text binding, and the value the binding will respond to. There are over twenty built in bindings to Knockout outside of the text binding.

The following view is based on the view model above:

<div class="box">
  <h1>Shake Shop</h1>

  <div>
    <!-- Bind the customer name to the value of the textbox -->
    Customer Name: <input data-bind="value: customerName">
  </p>
  <!-- Bind the visiblility of the p tag based on whether the customer's
  name is not empty. However, this will not work because the view cannot 
  get updates from the view model in its current state-->
  <p data-bind="visible: customerName.length > 0">

    Hello 
    
    <strong>
      <!-- Bind the customer name to the text of the span -->
      <span data-bind="text: customerName"></span>,
    </strong>
    
    we have the following shakes available:
    <!-- Bind the options for the drop down list from the string array -->
    <select data-bind="options: shakes"></select>
  </div>
</div>

The view above uses declarative binding in three places.

  • customerName is bound to the initial textbox.
  • The second div tag’s visiblity is bound by the condition that the customerName’s length is greater than zero.
  • The select element’s options are bound from the simple JavaScript array of shake types.

However, the customerName does not update when we change the value in the textbox. In order to get the value to update we need to implement an observable.

Observables

When you change the value of a property on the view you do change the underlying data model. In the previous sample, the view has no way of knowing how to update. Observables are functions that store the value of a specified property. They automatically notify the view when the value of the property has changed and force the view to change.

We need to assign customerName to an observable rather than a just a string.

function ShakeShopViewModel() {

  this.shakes = ['Vanilla', 'Chocolate', 'Harlem'];
  
  // change the customerName property to an observable with 
  // a default of an empty string.
  this.customerName = ko.observable("");
  
}

ko.applyBindings(new ShakeShopViewModel());

If you update the textbox bound to customerName the interface will automatically update. If this was written with jQuery we would be selecting elements, setting their properties, manipulating the select’s options manually, and attaching event handlers. But with Knockout, we can easily accomplish this task with a very small amount of code.

Parenthesises or not?

Since an observable is a function you need to add parenthesises on to the end of the property. Unless we are accessing the observable in a binding that is not evaluating an expression. The visible binding needs parenthesises since we are returning an expression to determine the element’s visibility Whereas, simply displaying a property with the text binding does not use parenthesises.

<!-- Parens are needed because we evaluating an expression -->
<div data-bind="visible: customerName().length > 0">

<!-- Parens are not used, no expression here -->
<input data-bind="value: customerName" />

Templating

In the old days of Knockout, it solely relied on script templates. This involved a lot clunky syntax, then setting a DOM element as a container, and linking it to the proper template. Nowadays, Knockout has made templating much simpler with the introduction of control flow bindings.

Control flow bindings

There are four different control flow bindings, but the main one we will be focusing on is the foreach binding. The foreach binding is attached to a DOM element that will act as a container. An array is passed into the foreach template and the child elements within the container are copied over the length of the array.

There are two situations to be aware of when using the foreach binding.

  • Binding within a natural container.
  • Binding when there is no container, or containerless binding.

Natural container

Control flow binding allows you to attach the foreach binding to a natural container such as a ul, ol, or tbody and specify the child element as the template.

<!-- Use native binding to bind the shakes to the ul -->
<ul data-bind="foreach: shakes">
  <li data-bind="text: $data"></li>
</ul>

Containerless

If there is no natural container available, Knockout allows you to declare a container with a special comment syntax.

<blockquote>
  <!-- ko foreach: shakes-->
  <div> - <span data-bind="text: $data"></span> </div>
  <!-- /ko -->
</blockquote>

The $data variable

In the above examples, $data is a special variable called a binding context. All $data means is “this item”. So when we are iterating through the foreach binding and creating each DOM element, $data represents the current item we are binding from the shakes array.

Our shakes array is pretty boring as just a string array. Let’s add more detail by turning it into an object.

Model Binding

Most of the time we will want to bind an array of objects to the view rather than primitives. This is easily achieved in Knockout by creating a model with a constructor function.

// Here we define our Shake object with a constructor function
function Shake(theName, thePrice){
  // These properties needn't be observables because we just 
  // want to bind them to the page, we could care less about
  // updating them in the future.
  this.name = theName;
  this.price = thePrice; // we've added a price
}

function ShakeShopViewModel() {

  // a simple array of Shake objects
  this.shakes = [
    new Shake('Vanilla', 5),
    new Shake('Chocolate', 7),
    new Shake('Harlem', 99)
  ];
  
  this.customerName = ko.observable("John Elway");
  
}

ko.applyBindings(new ShakeShopViewModel());

You can perform operations, or even write functions, inside of bindings. In the updated version of the view we concatentate the price with the name for display purposes.

<ul data-bind="foreach: shakes">
  <!--Concatenate observables to get the display we want-->
  <li data-bind="text: '$' + price + ' - ' + name"></li>
</ul>

Keep in mind, just because you can doesn’t mean you should. Concatenating the observables inside of the binding is a messy way of getting the data to display in a specific format. A better solution is to create a property inside of the Shake model that returns the formatted string we need. This will separate the logic out of the view.

function Shake(theName, thePrice){
  this.name = theName;
  this.price = thePrice;
  
  this.description = function() {
    return "$" + this.price + ' - ' + this.name;
  };
}

Remove the expression in the text binding and add the new description property. Note that we have to use the parenthesises because description is a basic JavaScript function. Therefore, to get the value we need to invoke the function.

<ul data-bind="foreach: shakes">
  <!--Bind the text value to the formatted string-->
  <li data-bind="text: description()"></li>
</ul>

When to use $data

Inside of a foreach binding, the default binding context is $data, so you usually don’t have to specify it unless you are binding an array of primitives, which don’t have a property name.

<ul data-bind="foreach: shakes">
  <li data-bind="text: $data.name"></li>
  <!-- The syntax above and below are equivalent -->
  <li data-bind="text: name"></li>
</ul>

Observable Arrays

An observable array is much like an observable. In fact, an observable array is basically an observable with the features of an array. Observable arrays are useful for binding a list of items to the view. Observable arrays provide the ability to automatically update values from inside of the array.

A nice feature to have for our Shake Shop would be the ability to place orders. To do this we can add an observable array that represents the ordered shakes to our view model. Every time a customer places an order, we want to display the order on the view. Observable arrays have the methods you would expect out of a normal array to get data in and out, so we will leverage those.

function ShakeShopViewModel() {
  // store a reference to this to avoid
  // confusion when defining properties and functions
  var self = this;
  
  self.shakes = [
    new Shake('Vanilla', 5),
    new Shake('Chocolate', 7),
    new Shake('Harlem', 9)
  ];
  
  // Initialize to an empty array
  self.orders = ko.observableArray([]);
  
  // This will add a shake on to the orders observable
  self.orderShake = function() {
    // this refers to the shake we are clicking on
    var currOrder = this;
    // we can't add "this" on because it is a reference, we need to create a new object
    var newOrder = new Shake(currOrder.name, currOrder.price);
    self.orders.push(newOrder);
  };
  
  // This will remove a shake from the orders observable
  self.removeShake = function() {
    var order = this;
    self.orders.remove(order);
  };
  
  this.customerName = ko.observable("John Elway");
  
}

var self = this;

Any one who has developed in JavaScript can tell you about the importance of the this keyword. In Knockout it is no less important. When defining functions within your models and view models this can get really confusing. Fortunately, there is an easy trick to help resolve the confusion.

At the beginning of your model or view model, simply assign this into a variable called self. From there on out you can use the self variable when defining or referencing properties and functions that belong to model or view model.

The click binding

In our view, we need to provide a button that allows the customer to place an order and an area for them to view each line item of their order as well. Instead of bullets for our list, we’ll use buttons that the user can click to add that specific shake to their order.

Knockout provides us with a binding for click events. Within the click binding we can call methods from our models and view models.

There is also an event binding that let’s you attach all of the JavaScript events such as change, mouseover, mouseout, and keypress. If you use jQuery solely for event handling, you’ll have one less file to reference when using Knockout.

<div class="box">
  <h1>Shake Shop</h1>

  <p>
    Customer Name: <input data-bind="value: customerName">
  </p>
  <div data-bind="visible: customerName().length > 0">
    <div>
      
      Hello 
    
    <strong>
      <span data-bind="text: customerName"></span>,
    </strong>
    
      we have the following shakes available:
    
    </div>
    
    <ul data-bind="foreach: shakes">
      <li>
        <button data-bind="click:$root.orderShake">+</button>
        <span data-bind="text: description()"></span>
      </li>
    </ul>
    
    <div>
      
      <!-- We can keep track of how many orders have been added without writing 
      any extra properties in the view model-->
      Orders - <span data-bind="text: orders().length"></span>
    
      <!--Bind the orders to the ul -->
      <ul data-bind="foreach: orders">
        <li>
          <!--Use a click binding to remove any unwated orders-->
          <button data-bind="click: $root.removeShake">x</button> - 
          <span data-bind="text: name"></span>
        </li>
      </ul>
      
    </div>
    
  </div>
</div>

The $root variable

We learned above that the $data variable is a binding context that means “this item”. The $root variable is also a binding context, but it’s meaning refers to the view model. Using the $root variable allows us to break out of the context of the item, which is the Shake object, and move to the top most level, the view model. With the $root variable we can access the properties and functions of the view model within any context.

We can now add and remove orders, but what if we want to see the total price of our order? Computed observables are just the tool for that.

Computed Observables

Observables automatically update whenever their values are changed. What if you need to do an operation on a observable so it will display in a certain format? A computed observable is a function that involves one or more observables. It will automatically update whenever one of its dependencies has changed.

To calculate the total price of all of the orders, we can write a computed observable that will update the total price every time an order is added.

// Use a utility function to get the sum of the
// price in the orders observable array
self.totalPrice = ko.computed(function() {
  var total = 0;

  // Knockout has a set of utility functions that save you 
  // from writing tedious tasks such as looping through an array
  // like below. This is similar to jQuery's $.each
  ko.utils.arrayForEach(self.orders(), function(shake) {
    var intPrice = parseInt(shake.price);
    total += intPrice;
  });

  // format the total
  return "$" + total;

  // We have to pass the self variable as the second
  // parameter here to define what the value of "this"
  // will be inside of the function
}, self);

To use the computed observable we will simply put it inside of a text binding.

<div class="box">
  <h1>Shake Shop</h1>

  <p>
    Customer Name: <input data-bind="value: customerName">
  </p>
  <div data-bind="visible: customerName().length > 0">
    <div>
      
      Hello 
    
    <strong>
      <span data-bind="text: customerName"></span>,
    </strong>
    
      we have the following shakes available:
    
    </div>
    
    <ul data-bind="foreach: shakes">
      <li>
        <button data-bind="click:$root.orderShake">+</button>
        <span data-bind="text: description()"></span>
      </li>
    </ul>
    
    <div>
      
      Orders - <span data-bind="text: orders().length"></span>
      
      <!--Display the total price that will update as orders get added-->
      <div>
        Total: 
        <span data-bind="text: totalPrice"></span>        
      </div>      
    
      <ul data-bind="foreach: orders">
        <li>
          <button data-bind="click: $root.removeShake">x</button> - 
          <span data-bind="text: name"></span>
        </li>
      </ul>
      
    </div>
    
  </div>
</div>

Now every time we add an order, the total price will update automatically.

Conclusion

Knockout is a simple to use JavaScript framework that has a ton of capabilities. Declarative binding is easy to use and it’s use is obvious to other developers reading the code. Observables allow us to create properties whose values will update on the view automatically. Control flow bindings allow you to bind data to the view using basic HTML syntax. When you need a tool that can create responsive interfaces without a lot of code, Knockout is ready to help.