Key Takeaways
- KnockoutJS is a JavaScript library based on the Model-View-ViewModel pattern, designed to create rich user interfaces with ease, especially for applications with sections that update dynamically when the underlying data model changes.
- Installing Knockout involves including a small JavaScript file in your HTML page, which can be downloaded from the Knockout website or included from a CDN.
- Observables in Knockout allow for two-way data binding, automatically updating the UI when the ViewModel property changes, and vice versa. Computed observables are functions that depend on one or more other observables, automatically updating when any of its dependencies change.
- Observable arrays in Knockout track changes to an array of values, making it easy to create dynamic, data-driven user interfaces. When a new item is added to the collection or an existing one is deleted, the view will automatically update itself.
- Knockout can be used alongside other JavaScript libraries, and is compatible with all modern browsers, including IE 6+, Firefox 3.5+, Chrome, Safari, and Opera.
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 AjaxGET
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 innerHTML
ed 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:- Detailed information on computed observables.
- The detailed declarative binding syntax.
- Working with JSON.
Frequently Asked Questions about Understanding Knockout
What is the main difference between Knockout and other JavaScript libraries?
Knockout is a JavaScript library that helps developers to create rich, responsive display and editor user interfaces with a clean underlying data model. Unlike other JavaScript libraries, Knockout uses a Model-View-ViewModel (MVVM) design pattern where the model refers to the data, the view is the visual representation of the data, and the ViewModel is the intermediary between the model and the view. This pattern allows for a clear separation of concerns, making it easier to manage complex applications.
How does Knockout handle data binding?
Knockout uses declarative bindings to connect parts of your UI to your data model. You can easily define complex relationships between view components using a concise, readable syntax. This allows you to keep your JavaScript code clean and manageable, even as your application grows in complexity.
What are computed observables in Knockout?
Computed observables are functions that are dependent on one or more other observables. Knockout automatically updates the computed observable whenever any of its dependencies change. This is a powerful feature that allows you to create complex behaviors in your application with very little code.
How can I download and install Knockout?
Knockout is available for download from the official website. You can also include it in your project using a CDN. Once downloaded, you can include it in your HTML file using a script tag.
What resources are available for learning Knockout?
There are many resources available for learning Knockout. The official website provides a comprehensive documentation, including a getting started guide, detailed API reference, and examples. There are also many tutorials and blog posts available online.
How does Knockout handle events?
Knockout provides a number of built-in event handlers that you can use to respond to user actions. These include click, submit, focus, among others. You can also create your own custom event handlers if needed.
Can I use Knockout with other JavaScript libraries?
Yes, Knockout is designed to be used with other JavaScript libraries. It does not make any assumptions about the rest of your technology stack, so you can easily integrate it with other libraries and frameworks.
How does Knockout handle arrays and collections?
Knockout provides observable arrays, which are a type of observable that tracks changes to an array of values. This allows you to easily create dynamic, data-driven user interfaces.
What is the performance of Knockout like?
Knockout is designed to be fast and efficient. It uses a number of optimization techniques to ensure that your application remains responsive even as it grows in complexity.
How is Knockout tested and what is its browser compatibility?
Knockout is thoroughly tested using a suite of automated tests. It is compatible with all modern browsers, including IE 6+, Firefox 3.5+, Chrome, Safari, and Opera.
Sandeep is the Co-Founder of Hashnode. He loves startups and web technologies.