Understanding Knockout

Sandeep Panda
Share

KnockoutJS is an elegant JavaScript library based on Model-View-ViewModel pattern that helps us create rich user interfaces effortlessly. If you are developing an application with sections that update dynamically whenever the underlying data model changes, then Knockout can really help you. The two way data binding and templating features in Knockout make the process of implementing dynamic views a breeze. This tutorial will get you started with Knockout, and show you how to use it in your own projects.

Installing Knockout

Installing Knockout is a matter of including a small JavaScript file in your HTML page. Head over to the Knockout website and download the production release. Alternatively, you can include Knockout from a CDN. Just place the following <script> tag in your HTML document.

<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js'></script>

The MVVM Pattern

To effectively use Knockout, you must first understand what the MVVM pattern is. If you already understand the MVVM patter, you can skip ahead to the next section.

Model: The M in MVVM stands for model, which is usually the application’s persisted business data. In most cases, you will initially read this data from the server through an Ajax call and display it on the UI. For example, if you want to retrieve a list of notes from the server you might make an Ajax GET request to the server.

View: In Knockout the view is simply an HTML page that displays the ViewModels (we will come to that). Whenever these ViewModels change, the specific portions of the view that are linked to the ViewModel also change.

ViewModel: In simple terms a ViewModel is the Model presented by the View. It’s a pure code representation of the data and the supported operations on it. The ViewModel is not usually persisted, and holds the unsaved changes the user is working with. If you want to save the changes later, you can post this data back to the server. In Knockout the ViewModels are implemented by POJOs (Plain Old JavaScript Objects). For instance, if you are displaying a list of todo notes, then your ViewModel might hold a list of such note objects and expose several functions to modify/add notes.

Getting Started

As our first step towards learning Knockout, let’s examine ViewModels and data binding. The following snippet creates a simple ViewModel:

function NameViewModel() {
this.name = 'John Doe';
}

Alternatively, the ViewModel can be written as an object, as shown below.

var nameViewModel = {
name: 'John Doe'
}

Now, in the HTML you just need to write the following declarative binding to connect with the name property of the ViewModel.

Hello, <span data-bind="text:name"></span>

This snippet simply connects the UI with the ViewModel’s name property. Here, the value of name is innerHTMLed into the span tag. Now, as a final step, we need to tell Knockout which ViewModel the name property belongs to. To do that, just add the following code.

ko.applyBindings(new NameViewModel());

This causes Knockout to perform data binding. As a result, in the HTML we see the value of name inside the span element.

Note: In complex applications you may have several ViewModels instead of just one. In that case you can bind a specific ViewModel to a particular part of the UI by passing a second argument to ko.applyBindings(). An example of this is shown below.

ko.applyBindings(new ContactViewModel(), document.getElementById('contacts-area'));
ko.applyBindings(new NoteViewModel(), document.getElementById('notes-area'));

One last thing to note is that you should not call ko.applyBindings() until the document is ready. If you are using jQuery, wrap the call inside $(document).ready(). In VanillaJS, you can use the DOMContentLoaded event handler.

Two Way Binding with Observables

We just learned how to bind a model property to the UI. However, we can go even further and make this thing dynamic. With observables you can bind a property to the UI with one additional benefit – whenever your ViewModel property changes, the UI is updated automatically. Additionally, we can use Knockout’s declarative binding feature so that the ViewModel property is also updated whenever the value in the UI (e.g. input field value) changes. This keeps your ViewModel and View in sync.

To update the view according to the property value, you need to make the property observable. This is pretty simple. Just modify our previous code to look like this:

function NameViewModel() {
this.name = ko.observable(''); //initially empty
}

Our view will now be updated whenever the name property changes. Now, let’s add an input field to the HTML and bind it to name so that whenever a user types into it, the property changes, and we see the updated value in the span tag.

<input type="text" data-bind="value:name,valueUpdate:'input'" placeholder="start typing a name here"/>
Hello, <span data-bind="text:name"></span>

Here, we are utilizing Knockout’s declarative binding syntax. In the data-bind attribute, the value indicates which property we want to bind to. The second parameter, valueUpdate, specifies when to update the ViewModel property. As we have set it to 'input', the property in the ViewModel will be updated whenever the input field value changes. To see this feature in action, take a look at this plunker.

You can also get notifications whenever any observable value changes. The following example shows how this is done using the subscribe() function.

function NameViewModel() {
this.name = ko.observable('');
this.name.subscribe(function(newVal) {
console.log(newVal); //logs whenever the value changes
});
}

Working with Computed Observables

Sometimes you might want to use a derived property whose value depends on one or more other properties. If you are displaying this derived property in the view, it is logical to update it whenever the properties it depends on change. To do that we need a computed observable, which is created like this:

function ContactViewModel() {
this.phone = ko.observable();
this.email = ko.observable();

this.contactInfo = ko.computed(function() {
return this.phone() + ", " + this.email();
}, this);
}

Now in the view we can bind the computed observable using the following HTML.

Contact Information: <span data-bind="text: contactInfo"></span>

By looking at the callback (or evaluator function) you pass to ko.compute(), Knockout knows which observables your computed observable depends on. Whenever any of them change, your evaluator function gets called automatically.

The second parameter to ko.compute() is the object that should be used as this inside your evaluator function. It is worth pointing out that you can implement the same functionality using closures, as shown below.

function ContactViewModel() {
var self = this;

self.phone = ko.observable();
self.email = ko.observable();

self.contactInfo = ko.computed(function() {
return self.phone() + ", " + self.email();
});
}

The last thing to note is that when you make your properties observable, you should no longer access them directly in your code. Instead, we need to call them like functions. That’s why the values of phone and email were retrieved using function calls in the previous example. To set the value of an observable, simply pass the new value as a function argument (i.e. self.phone(4657324573)).

Observable Arrays

Whenever we want to detect changes in a single ViewModel property, observables are the way to go. But, in many scenarios we are interested in knowing if a collection of objects has changed. In such cases we can use observable arrays, which are created using the following code.

function NotesViewModel() {
this.notes = ko.observableArray();
}

After creating an observable array, you will typically loop through the items and display them on the UI. Whenever you add a new item to the collection or delete one, your view will automatically update itself.

You can also pass an initial value to your observable array like this:

this.notes = ko.observableArray(['one', 'two', 'three']);

You can also perform a variety of array operations such pop(), push(), shift(), unshift(), reverse(), sort(), splice(), etc. Sweet, isn’t it?

A Simple Knockout App

In this section we will create a Knockout app with a simple UI for managing cell phone data. When a new cell phone is added, the details will displayed in a table. The user can also delete an item from the table. Take a look at the demo which shows the end result!

Before going any further, make sure you have both Knockout and the Knockout mapping plugin installed. I will talk about the mapping plugin later. For now, just include it like this after the Knockout library:

<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"/>

Step 1

Let’s assume each cell phone will have three properties: name, os, and price. We need to create a ViewModel, and add a few sample phone details:

function PhonesViewModel() {
var self = this;

self.phones = ko.observableArray([{
name: 'Sony Xperia Z1',
os: 'Android',
price: 599
}, {
name: 'Apple iPhone 5S',
os: 'iOS',
price: 199
}, {
name: 'Google Nexus 5',
os: 'Android',
price: 299
}]);
}

As you can see, we have created an observable array that holds cell phone details. We will loop through these array elements on the view.

Step 2

Create an object to hold the current phone details being entered on the view. We’ll need to add the following code to our ViewModel.

self.currentPhone = ko.mapping.fromJS({
name: '',
os: '',
price: ''
});

The currentPhone object holds the phone details that are being entered on the UI. We bind the properties name, os, and price to the input fields on the HTML. We also want to clear the fields once the phone details are added. In order to clear the fields we need to make these properties observable and subsequently clear them inside the function where we add the phone. The ko.mapping.fromJS() function from the Knockout mapping plugin automatically makes the object properties observable so that we don’t have to write ko.observable() for each property.

Step 3

Next, we want to provide a means for adding a new phone to our list. Just add the following function to our ViewModel.

self.addPhone = function() {
self.phones.push(ko.mapping.toJS(self.currentPhone));
self.currentPhone.name('');
self.currentPhone.os('');
self.currentPhone.price('');
};

ko.mapping.toJS() creates and returns an object with normal properties rather than observables. We then add this object to our list of phones and clear the properties of currentPhone so that it reflects in the view.

Step 4

In this step we’ll allow the user to delete phones from the list. This is accomplished using the following function.

self.removePhone = function() {
self.phones.remove(this);
};

Here, this represents the particular row in our table that is going to be deleted.

Step 5

Next, add the following markup:

<table>
<thead>
<tr>
<td></td>
<th>Name</th>
<th>OS</th>
<th>Price</th>
</tr>
</thead>
<tbody data-bind="foreach: phones">
<tr>
<td><a href="#" data-bind="click: $parent.removePhone">Remove</a></td>
<td data-bind="text: name"></td>
<td data-bind="text:os"></td>
<td data-bind="text:price"></td>
</tr>
</tbody>
</table>
<hr/>
<h3>Add a new Phone</h3>
<form data-bind="submit:addPhone">
<input type="text" data-bind="value:currentPhone.name,valueUpdate:'input'" placeholder="Phone Name" />
<br/>
<input type="text" data-bind="value:currentPhone.os,valueUpdate:'input'" placeholder="OS" />
<br/>
<input type="text" data-bind="value:currentPhone.price,valueUpdate:'input'" placeholder="Price" />
<br/>
<button type="submit">Add</button>
</form>

In this markup we have used foreach data binding that iterates over the list of phones and displays them. We have also used click binding to remove an item from the table. This invokes the removePhone() function on our ViewModel. As we are inside foreach binding, a new context has been created for us. To obtain a reference to the root ViewModel we use $parent.

The next thing to note is the submit binding. This prevents the usual form submission process when the submit button is clicked. This lets us specify a custom function that will be called instead. In this case, addPhone() is called, adding a new phone. Inside the form, we have three input fields which are in sync with properties of currentPhone. So, as soon as someone presses the submit button, we have the details in the currentPhone object, which just needs to be pushed to our list of phones.

Step 6

Activate Knockout using the following code and watch everything work!

ko.applyBindings(new PhonesViewModel());

Conclusion

KnockoutJS is definitely a great framework for building dynamic UIs using the MVVM pattern. This tutorial covered the basics of the library, but there are certainly advanced concepts which you should be aware of. The following resources can help you out:

The complete source code for this article’s demo can be found on GitHub.