If you are doing any client-side JavaScript coding these days, especially single-page applications, you would be crazy not to take advantage of at least one of the plethora of available JavaScript libraries or frameworks. One of the primary benefits of these libraries is data binding and, in particular, two-way data binding. Implementing two-way, declarative data binding can significantly reduce your codebase as well as make your web application much easier to read and understand.
Two of the most well-known libraries that implement two-way data binding are AngularJS and KnockoutJS. While AngularJS is becomingly increasingly popular and provides a lot more functionality than KnockoutJS it is undoubtedly more complicated, harder to get up and running with and partly shoehorns you, the developer, into its own framework or architecture. On the other hand, KnockoutJS essentially does just one thing, data binding, and does it well. However, as you will see, this one thing can make a big impact on the readability and maintainability of your web application.
Data binding with HTML and JavaScript is a very simple concept at its core. It provides a way to associate DOM elements (HTML) with JavaScript objects or variables and keeps them in synch with each other such that when one of them changes the associated entity automatically changes as well. For example, when the user edits the value in an <input>
HTML element that is bound to a JavaScript variable (or data location) the variable is updated as well. There is no need for you to develop the code that, upon page submit for example, finds the correct DOM element and copies the appropriate value to the JavaScript variable or object.
Now that you have a basic understanding of data binding let’s get more pragmatic and write some code that takes advantage of what KnockoutJS has to offer. We will start with a simple example that actually only represents one-way data binding.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script type="text/javascript" src="jquery-1_11_2_min.js"></script>
<script type="text/javascript" src="knockout-3.3.0.js" ></script>
<script>
var theViewModel = null;
$(document).ready(function ()
{
theViewModel = ViewModel();
ko.applyBindings(theViewModel);
});
function ViewModel()
{
var self = this;
self.FirstName = "Tom";
self.LastName = "Brady";
return self;
}
function firstName_OnBlur()
{
alert(theViewModel.FirstName);
}
</script>
</head>
<body>
<div>
<label>First Name:</label>
<input type="text" data-bind="value: FirstName" onblur="firstName_OnBlur();" />
</div>
<div>
<label>Last Name:</label>
<input type="text" data-bind="value: LastName" />
</div>
</body>
</html>
There are three items in the above example that are specific to KnockoutJS. Two of them are the data-bind
attributes on the two <input>
HTML elements declaratively binding the element to JavaScript. The data-bind
attribute will always consist of a property name for the associated element followed by a colon and the JavaScript that the property should bind to (you may also bind more than one property in the same attribute). This JavaScript could be a JavaScript expression but will most often be an object property.
The third KnockoutJS-specific item is the JavaScript call to the ko.applyBindings
function in the $(document).ready
function. The applyBindings
function tells KnockoutJS to associate the UI to the given JavaScript object. In this example any HTML element can be bound to any properties of the ViewModel object. However, it is possible to associate multiple JavaScript objects to different parts of the page by passing in an element as the second parameter to ko.applyBindings
. By default, when nothing is passed in for the second parameter, the bindings apply to the whole page.
You will notice in the above example, when you run it, that the alert in the blur handler for the “first name” input element will always display the original value of the textbox, which is ‘Tom’ in this case. This is because the above example only implements one-way data binding. Implementing two-way binding is as simple as calling a function on the KnockoutJS library to indicate that the binding should be ‘observable’, meaning that the associated entity should be notified and updated when the corresponding entity is updated. Replace the ViewModel
function with the following:
function ViewModel()
{
var self = this;
self.FirstName = ko.observable("Tom");
self.LastName = ko.observable("Brady");
return self;
}
The observable
function is the function to call if you want two-way binding on the UI element. The single parameter is the default value. Now if you run the example using the modified ViewModel
function, the alert in the firstName_OnBlur
function will display the updated value of the <input>
element via the JavaScript object variable. One other thing to note is that when you ‘observe’ an object KnockoutJS will actually create a function for the underlying JavaScript object. Therefore, in order to access the value of the object (or variable) you need to treat is as a function by applying ‘()’ to the end of the object. In the example above the call to ‘alert’ would change as follows:
alert(theViewModel.FirstName());
Setting the value of the object would be similar in that you need to provide the value as a parameter to the function, as follows:
theViewModel.FirstName(‘Bill’);
This is the only ‘gotcha’ with KnockoutJS and, I believe, it’s implemented this way in order to support all versions of JavaScript and browsers.
The next logical question from any developer may be “How do I implement iterative binding such that a dynamic list of HTML elements can be bound to a list of JavaScript objects (or array)?” This is nearly as simple. Instead of calling the ‘observable’ function you would call the observableArray
function in KnockoutJS, as in the following example.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script type="text/javascript" src="jquery-1_11_2_min.js"></script>
<script type="text/javascript" src="knockout-3.3.0.js" ></script>
<script>
var theViewModel = null;
$(document).ready(function ()
{
theViewModel = ViewModel();
ko.applyBindings(theViewModel);
});
function Car(make, model)
{
var self = this;
self.Make = ko.observable(make);
self.Model = ko.observable(model);
}
function ViewModel()
{
var self = this;
self.Cars = ko.observableArray([
new Car("Honda", "CR-V"),
new Car("Toyota", "Sienna"),
new Car("Ford", "Explorer")
]);
self.onClick = function ()
{
alert(this.Make() + ' ' + this.Model());
};
return self;
}
</script>
</head>
<body>
<div>
<label>Colors:</label>
<table>
<thead>
<tr>
<th>Make</th>
<th>Model</th>
</tr>
</thead>
<tbody data-bind="foreach: Cars">
<tr data-bind="click: onClick">
<td><input data-bind="value: Make" /></td>
<td><input data-bind="value: Model" /></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
There are a few things to take note of in this example. The foreach
keyword in the data-bind for the <tbody>
element is the KnockoutJS way to iterate over an array. By default, any descendant HTML elements are bound to elements in the array, not the parent object (in this case the ViewModel), which explains why we can reference the Make and Model properties in the data-bind attributes.
The other item to notice is the reference to ‘click’ in the data binding for the table row element. KnockoutJS also allows you to bind certain events to functions within the data model. In the example above we associate the onClick
function in ViewModel
(the parent in this case) with the click event for each table row. In this case the scope, referenced by ‘this’, inside this function is the array element itself.
You will notice in both of the examples above that an ‘id’ or ‘name’ attribute is never assigned to any of the HTML elements. This is because there is no need to. If you are implementing data binding correctly your JavaScript code should not need to reference any of the elements in the DOM. Instead you will reference the JavaScript variables/objects in order to get the values of these elements. KnockoutJS takes care of the rest.
There is one more piece of functionality, named ‘computed observables’, as part of KnockoutJS that I would like to point out as it really rounds out the usefulness and power of this library as well as the power of data binding. This functionality provides a way to do two-way data binding using more than one observable. Specifically, it allows you to associate a JavaScript function to a DOM element rather than a variable or object property so that the value can be ‘computed’ rather than just assigned.
As an example of a computed observable we can use the first demo above and add a FullName
property to the ViewModel
object that is computed by concatenating the first and last name properties (with a space in between of course), as follows:
function ViewModel()
{
var self = this;
self.FirstName = ko.observable("Tom");
self.LastName = ko.observable("Brady");
self.FullName = ko.computed(function ()
{
return self.FirstName() + " " + self.LastName();
});
return self;
}
We can test the new FullName
observable by modifying the firstName_OnBlur
event function to display the full name, as follows:
function firstName_OnBlur()
{
alert(theViewModel.FullName());
}
Those developers familiar with creating single-page web applications should understand the power that the KnockoutJS library provides, particularly considering its small footprint and limited functionality. While there is more functionality that KnockoutJS provides, like custom bindings, custom elements and subscribing to changes, this article covered the most important and powerful features and should help you to start (data) binding right away.