Friday, 4 July 2014

KnockoutJS

In this blog I am going to give basic information which helps you to start working with knockoutJS.
Below are the concepts that we will be covering here.
  • What is knockoutJS
  • MVVM pattern
  • Starting with knockoutJS
  • How to do things with and without knockoutJS
  • Observables
  • Computed Properties
  • ObservableArray
  • Built in Bindings
  • Templates
  • Custom binding handlers
What is knockoutJS

From the official site, Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model. Any time you have sections of UI that update dynamically (e.g., changing depending on the user’s actions or when an external data source changes), KO can help you implement it more simply and maintainably.
It follows MVVM pattern.
Entirely written in Javascript.
Wide browser compability.

MVVM pattern

It is abbreviation of Model-View-ViewModel. In MVVM, UI layer will be separated into physical and logical layers.
VM (view-model) is the Logic layer. This, in general is a JavaScript function. It is a UI layer set of classes. It is a logical representation of User interface. We can think of UI in logical terms like properties/collections instead of textboxes or listboxes.
V(View) is the Physical layer.
M(Model) is the actual data.

Starting with knockoutJS

Before we get started with knockout, we need to download knockoutJS script file. We can get it from http://knockoutjs.com/downloads/index.html .Just refer it in the file where we need knockoutJS functionality using the below code
<script type='text/javascript' src='knockout-2.1.0.js'></script>

HTML5 “data-“ tags will be used by Knockout. How to use KnockoutJS will be discussed using the following 3 steps.

1. Declarative binding:
We use data-bind (data- attribute which is the feature of html5) attribute to work with knockout. We will be giving a key value pair to data-bind attribute. This key-value pair specifies what item should I use to what type of binding.
Lets consider the below example:
<input data-bind="value: name" />
Here I am binding name item (property) to value type of binding.
Note: Type of bindings are casesensitive. Specifying Value instead of value will lead to unexpected results.

2. Creating a ModelView that can be used in the declarative binding:
As we have discussed previously, ModelView is just a javascript function. Consider the following modelview object
var personModel = 
{
name : ko.observable("person1");
age : 20;
}
Here ko is a knockout defined object and observable is a special function of knockout. Observable means, if the value changes on UI, ko tells the underlying model that the value got changed and update all its references. In the similar way if the underlying value changes, it tells the UI that the underlying value got changed and get updated accordingly. This is nothing but a two-way binding. These observable properties are monitored by the framework to keep UI in sync with the ViewModel.
In the example, name is an observable property and age is not.
Consider the property age. This is not observable. If there are any updates done on that property, it will not be reflected. It just sends the data to the UI and not back. If we need any UI part associated with age property updated accordingly, we need to do it explicitly using JQuery or some other way. This is only one way binding. These kind of variables can be mostly used as readonly variables. There will be no events that will be invoked for this kind of properties.

3. Activating knockout:
This is the last and the most important step that we should not forget. After we are done with the above two steps, we need to bind the viewmodel to the view. This can be done using the following code snippet usually written in document.ready of the page.
ko.applyBindings(personModel);
applybindings can have an optional second parameter too. If we don’t specify the second parameter, it says to bind the viewmodel to the entire document. The second parameter when specified, helps us to bind a viewmodel to a specific section (say to a single div in the entire document). For example, the below code applies bindings to element with id "div1" only.
ko.applyBindings(personModel,document.getElementById("div1"));

How to do things with and without knockoutJS
Let’s see with an example how a task can be done using knockout and JQuery. For now just look into the amount of code that we write for both. We will do an example in later stages.
The two code snippets below renders a text box and a message Welcoming the user (name given in the text box) using knockout and JQuery.
Using knockout:
<html>
<head>
<title>Using Knockout</title>
</head>
<body>
<div id="div1">
<span>FirstName: </span>
<input type="text" data-bind="value :firstName" /><br />
Welcome <span data-bind="text: firstName"></span>. </div>
</body>
<script type='text/javascript' src='knockout-2.1.0.js'></script>
<script type="text/javascript">

function EmployeeViewModel() {
this.firstName = ko.observable();
}

ko.applyBindings(new EmployeeViewModel(), document.getElementById("div1"));
</script>
</html>

Using JQuery:
<html>
<head>
    <title>Using Knockout</title>
</head>
<body>
    <div id="div1">
        <span>FirstName: </span>
        <input type="text" id="txtFirstname"/><br />
        Welcome <span id= "spanFirstname"></span>.
    </div>
</body>
<script src="jquery-1.7.2.min.js" type="text/javascript"></script>
<script type="text/javascript" >

    $(document).ready(function () {
        $("#txtFirstname").blur(function () {

            $('#spanFirstname').text($('#txtFirstname').val());
            
        });
    });

</script>
</html>

In JQuery we need to handle onblur event dynamically, whereas in Knockout, the framework takes care of everything. This is just one of the example.
Note: Knockout is not a competitor for JQuery. It just does some of the task easy.

Observables

As I have already discussed that observables are special knockout functions. When a property is specified as "observable", they get the ability to detect changes and update the view or the underlying viewmodel accordingly.
Reading and writing observables
Lets check the below example to know how to read an observable.
Here is my updated view model.
function PersonViewModel()
{
    firstname = ko.observable('John');
    lastname = ko.observable('L');
    score = ko.observable(25);
    fullname = ko.computed(function () {
        return firstname() + ' ' + lastname();
    });
}
Below is my view (html page):

First Name: <span data-bind="text: firstname"></span><br />

Score: <input type="text" data-bind="value: score, style:{ color: score() < 20 ? 'red' : 'black' }" /><br />

Hi <span data-bind="text: fullname"></span> Your Score is <span data-bind="text: score, style:{ color: score() < 20 ? 'red' : 'black' }"></span>
If you see the view model part, observables are used as functions(look at the fullname).
In View, when a observable is used in any expression, if will be used as a function.
Suppose we need to update score value, this can be done using personViewModel.score(30);

Computed Properties
Computed properties are those which will be updated whenever value of an observable changes.
These will be defined using knockout's computed function. In the previous example, fullname is a computed property which will be recalculated when ever either firstname or lastname changes.
Observable Array

Similar to observables, observableArrays will be automatically reflected when any change occurs. If we have only one object that changes, we can use observables and if have a list of objects we can go with observableArrays.
This can be useful in working with a list of items.
Lets consider an example to display a list of food items using an observableArray. This can be done as below.
I have updated my view model to include 2 extra properties.
fooditems = ko.observableArray(['Pizza', 'Sandwitch', 'Burger', 'Veg-Roll', 'Fruits', 'Vegetables']);
selectedfooditem = ko.observable('');
and below is my updated view
<select data-bind="options: fooditems , value: selectedfooditem, optionsCaption: 'Choose...'"></select>
<span data-bind="text: selectedfooditem"></span>
If you look into the above change, I am using options binding to bind the fooditems observable array to the dropdown. value binding is used to get the selected item (Here selectedfooditem observable will be updated whenever selection changes).
Whenever you are trying to bind an observablearray of objects to the dropdown, you will need to specify which property of that object should be shown in the dropdown. This can be done using optionsText binding. We will specify the property name like in the example below:
Here is my updated View model part:
fooditems = ko.observableArray([{ item: 'Pizza', price: 30 }, { item: 'Sandwitch', price: 50 }, { item: 'Burger', price: 60 }]);
selectedfooditem = ko.observable('');

Here is my updated view part:
<select data-bind="options: fooditems , optionsText: 'item', value: selectedfooditem, optionsCaption: 'Choose...'"></select>
<span data-bind="text: selectedfooditem().item"></span>
Important Note:
  • Dont forget to put single-quotes for the property name used for optionsText
  • We have used selectedfooditem().item to get the fooditem name in the span
Built in Bindings
The following bindings are available in knockout.js
Bindings that control text and appearance
  • visible: This binding causes the associated DOM element to be hidden or shown based on the value we pass
  • text: This binding causes the associated DOM element to display the text that is passed.
  • html: Similar to text binding. But renders the value as html.
  • css: Adds or removes the css class specified.
  • style: If we want to set style instead of css class, we can use this
  • attr: In knockout.JS only few attributes can be bound directly. Suppose if we want to bind 'src' of img tag, we can use this attr binding. This can be used as data-bind="attr:{src: imagePath}". Here imagePath is a property in the viewmodel.
Templates
As the name suggests Templates are reusable code. A part of code can be repeated in multiple places which helps in reducing duplicate code.
Templating can be done in the following ways.
Native Templating:
Native templating is nothing but using templating concept as inline feature. We can use a foreach loop to have an inline templating.
For example:
<div data-bind="foreach: fooditems">
        <span data-bind="text: item"></span><br />
        <span data-bind="text: price"></span><br />
</div>
If you see the above example, spans will be repeated for all the items in the array fooditems. This means we are reusing the same part of code again and again.
String based Templating:
Here we will be using an external template, pass our model values to the template and inject the resulting markup in the document. This will be generally written in script tags. If we have some part of code that will be used in multiple places, we simply create a template for it and use it at all the required places. template binding will be used in this case. Consider the following example.
<body>
<div data-bind="template: { name: 'display-template', data: {firstname: 'John', age : 22 } }"></div>
<div data-bind="template: { name: 'display-template', data: {firstname: 'Mark', age : 19 } }"></div>
</body>
</html>
<script src="Scripts/knockout-3.1.0.js"></script>
<script src="Scripts/jquery-2.1.1.js"></script>
<script type="text/html" id="display-template">
Hi <span data-bind="text: firstname"></span> Your age is <span data-bind="text: age"></span>
</script>
<script>
function MyViewModel() {
var self = this;
}
$(function () {

var myvm = new MyViewModel();
ko.applyBindings(myvm);

});
</script>
If you see the above code, the template with name 'display-template' is used. This display-template is defined in script tags with the same as id. Whereever we use the template binding with the templatename as 'display-template', the script will be rendered as the result. As in the above example, we can pass parameters using the 'data' attribute and access them in the template declaration.

Custom Binding Handlers
Go for custom binding when can't find the right binding that you want to use. Custom bindings can go handy when you want to do the following.
  • Integrate some third party widgets with knockout.
  • Update built-in bindings to give more functionality.
Lets look at custom bindings a bit closer.
All bindings in knockoutJS come from bindingHandlers. It mainly provides 2 callback methods.
  1. init: knockout calls this function for every DOM element that you use this binding for.
  2. update: knockout calls this function along with init function and also whenever there are any changes to the observables that we access.
Basic syntax of custom binding:
ko.bindingHandlers.yourBindingName = {
init: function(element, valueAccessor, allBindings, bindingContext) {
// knockout calls this function for every DOM element that you use this binding for.
},
update: function(element, valueAccessor, allBindings, bindingContext) {
// knockout calls this function along with init function and also whenever there are any changes to the observables that we access.
}
};
The parameters to these functions are optional.
  • element: You might need to know the element that invoked this binding. In this case, you can use element parameter.
  • valueAccessor: Used to get current viewmodel property bound to this element.
  • allBindings: Used to get values of all the bindings associated to the DOM element.
  • bindingContext: Used to get the bindingContext of the currently bound element.
Integrate third party widgets with knockout
We can integrate third party tools like JQuery UI with knockoutJS. All we have to do is create a custom binding to that. Lets consider an example to use JQuery datepicker.
Consider the below code:
<script src="Scripts/knockout-3.1.0.js"></script>
<script type="text/javascript">

    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

            var dateFormat = allBindings().dateFormat;
            var buttonImage = allBindings().buttonImage; // allBindings.get('dateFormat');

            if (typeof dateFormat == 'undefined') {
                dateFormat = 'mm/dd/yyyy';
            }

            if (typeof buttonImage == 'undefined') {
                buttonImage = "Images/icon_date_picker.png";
 
            }

            var options = {
                showOtherMonths: true,
                selectOtherMonths: true,
                dateFormat: dateFormat,
                buttonImage: buttonImage,
                showOn: "both"
            };

            if (typeof valueAccessor() === 'object') {
                $.extend(options, valueAccessor());
            }

            $(element).datepicker(options);
        },

        update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

            var v = valueAccessor()();
            bindingContext.$data.message('You selected '+v);
        }

    };

    var viewModel = {
        myDate: ko.observable(''),
        dateFormat: ko.observable('mm/dd/yyyy'),
        message : ko.observable('')
    };

    $(document).ready(function () {
        ko.applyBindings(viewModel);
    });
    
</script>

<span data-bind="text: message"></span><br />
<input data-bind="datepicker: myDate, value: myDate,defaultDate: '', dateFormat: 'dd, MM yy'" />
If you see the above example, I have created a custombinding named 'datepicker'. In the init function, I have used 'allBindings()' to get the required bindings associated with this element. Finally I am using those bindings as input values to options. Using $(element).datepicker(options); statement, we are adding JQuery datepicker using knockoutJS Update function will be invoked whenever there is a change in the value bound to the element i.e.., when we select any date from the datepicker.
Update built-in bindings to give more functionality:
As discussed in "Built in bindings" section, In knockoutJS only few attributes can be bound directly. Suppose if we want to bind 'src' of img tag, we can use this attr binding. Instead of using attr binding with src property, we can create a custom binding so that it can be used whenever we want to set src for image tag.
This can be done as below
ko.bindingHandlers.customImgSrc = {
update: function (element, valueAccessor) {
ko.bindingHandlers.attr.update(element, function () {
return { src: valueAccessor() }
});
}
};
From the above custom binding, we can see that we are using 'update' to the built-in binding 'attr'. Now, the UI binding can be as below:

<img data-bind="customImgSrc: myImage" />
or
<img data-bind="customImgSrc: 'Images/icon_date_picker.gif'" />
instead of
<img data-bind="attr:{ src: 'Images/icon_date_picker.gif' }" />

That's all from my side. If you find anything intersting in knockoutJS, try sharing it.
Let me know if you have any suggestions.