Knockout is a JavaScript library that helps you to create rich, responsive displays and editor UIs with a clean underlying data model. It allows for a graceful sync between the UI and the model.
Let us first look at the main features of Knockout.js, which include:
- Dependency tracking
- The flexibility to update the appropriate sections in the UI as and when required
- Declarative binding
- Extensibility
How is Knockout different?
Knockout doesn’t compete with jQuery or other such similar low-level DOM APIs. It provides a complementary, high-level way to link a data model to a UI. Knockout (KO) itself doesn’t depend on jQuery, but you can certainly use jQuery at the same time.
Knockout uses the MVVM design pattern.
Model-View-View Model (MVVM) is a design pattern for building user interfaces. It allows you to keep a potentially sophisticated UI simple by splitting it into three parts.
- Model: This is your application’s stored data. This data represents objects and operations in your business domain (e.g., the employee information of a company) and is independent of any UI.
- View model: This is a pure code representation of the data and operations on a UI. For example, if you’re implementing a list editor, your view model would be an object holding a list of items, and exposing methods to add and remove items. Do note that this is not the UI itself — it doesn’t have any concept of buttons or display styles. It’s not the persisted data model either — it holds the unsaved data the user is working with. When using KO, view models are pure JavaScript objects that hold no knowledge of HTML. Keeping the view model abstract in this way lets it stay simple, so you can manage more sophisticated behaviour without getting lost.
- View: This is a visible, interactive UI representing the state of the view model. It displays information from the view model, sends commands to it (e.g., when the user clicks on a button), and updates whenever the state of the view model changes.
When using KO, your view is simply an HTML document with declarative bindings to link it to the view model. Alternatively, you can use templates that generate HTML, using data from your view model.
To create a view model with KO, declare any JavaScript object. For example:
var employeeModel={ name:’Anna’, department:’Sales’ };
You can then create a very simple view of this view model using a declarative binding. For example, the following markup displays the department value:
<p>The employee belongs to the department <span data-bind=”text: department”></span></p>
Downloading and installing Knockout
Get the latest version of Knockout.js from https://knockoutjs.com/downloads and reference it using a <script> tag somewhere on your HTML pages. For example:
<script type=’text/javascript’ src=’knockout-3.5.1.js’></script>
Update the src attribute to match the location where you put the downloaded file.
Activating Knockout
The data-bind attribute isn’t native to HTML, though it is perfectly okay (it’s strictly compliant in HTML 5 and causes no problems with HTML 4 even though a validator will point out that it’s an unrecognised attribute). But since the browser doesn’t know what it means, you need to activate Knockout to make it take effect.
To activate Knockout, add the following line to a <script> block:
ko.applyBindings(myViewModel);
You can either put the script block at the bottom of the HTML document, or you can put it at the top and wrap the contents in a DOM-ready handler such as jQuery’s $ function.
Now the view will be displayed as if you’d written the following HTML:
The name is <span>Bob</span>
What does ko.applyBindings do?
The first parameter says what view model object you want to use with the declarative bindings it activates.
Optionally, you can pass a second parameter to define which part of the document you want to search for data-bind attributes. For example:
ko.applyBindings(myViewModel,document.getElementById(elemenId))
This restricts the activation to the element with ID elemenId and its descendants, which is useful if you want to have multiple view models and associate each with a different region of the page.
Observables
One of the key benefits of KO is that it updates the UI automatically when the view model changes. In order for KO to know when parts of your view model change, declare your model properties as observables, because these are special JavaScript objects that can notify subscribers about changes, and can automatically detect dependencies.
For example, rewrite the preceding view model object as follows:
var employeeModel={ name:ko.observable(‘Anna’), department:ko.observable(‘Sales’) };
You don’t have to change the view at all — the same data-bind syntax will keep working. The difference is that it’s now capable of detecting changes, and when it does, it will update the view automatically.
Observables can be set up on a collection as well. These are called observable arrays. Similarly, KO has the concept of computed observables which are functions that are dependent on one or more other observables, and will automatically update whenever any of these dependencies change.
For example, given the following view model class…
function AppViewModel() { this.firstName = ko.observable(‘Bob’); this.lastName = ko.observable(‘Smith’); }
…you could add a computed observable to return the full name, as follows:
function AppViewModel() { // ... leave firstName and lastName unchanged ... this.fullName = ko.computed(function() { return this.firstName() + “ “ + this.lastName(); }, this); }
Now you could bind UI elements to it, as follows:
The name is <span data-bind=”text: fullName”></span>
…and they will be updated whenever the firstName or lastName changes (your evaluator function will be called once each time any of its dependencies change, and whatever the value you return will be passed on to observers such as UI elements or other computed observables).
Dependency tracking
Now that we know that there are observables and computed observables, let us look at how the dependency tracking works in Knockout at a high level.
The tracking algorithm goes like this:
1. Whenever you declare a computed observable, KO immediately invokes its evaluator function to get its initial value.
2. While the evaluator function is running, KO sets up a subscription to any observable (including other computed observables) that the evaluator reads. The subscription callback is set to cause the evaluator to run again, looping the whole process back to Step 1 (disposing of any old subscriptions that no longer apply).
3. KO notifies subscribers about the new value of the computed observable.
So, Knockout doesn’t just detect dependencies the first time the evaluator runs — it re-detects them every time. This means, for example, that the dependencies can vary dynamically: Dependency A could determine whether the computed observable also depends on B or C. Then, it will only be re-evaluated when either A or your current choice of B or C changes. You don’t have to declare dependencies — they’re determined at runtime from the code’s execution. If the evaluator doesn’t access any observables, the computed observable will have no dependencies and won’t ever need to call the evaluator function again. In that case, to save resources, the computed observable will be automatically ‘disposed’ of.
The other neat trick is that declarative bindings are simply implemented as computed observables. So, if a binding reads the value of an observable, it becomes dependent on that observable, which causes that binding to be re-evaluated if the observable changes.
This article covers the basics of KO for beginners. https://knockoutjs.com offers in-depth documentation as well as interactive tutorials for a better understanding. From my personal experience of working with KO, I would say that it can get pretty tricky when you have complex bindings and computations in place. Templating and custom binding can help save time and make things more standardised.