Understanding click binding in Knockout.js

Click Binding adds click event to element on View. You can do click binding with any function in ViewModel or you can bind to any normal function.

clip_image001

Let us try to understand it with a very simple ViewModel. We have a ViewModel as below

 var viewModel =
 {
 display: function ()
 {
 console.log("display");
 }
 }

As you see there is one function as property of ViewModel and that can be click bind on view as below,


<button data-bind="click:display" >display</button>

Let us consider one more example and in this we will update value of other observable on click of element on View. We have ViewModel as below,


var viewModel =
 {
 count : ko.observable(0) ,
 display: function ()
 {
 this.count( this.count() + 1);
 }
 }

On View we are doing click binding and text binding as below


<button data-bind="click:display" >display</button> <br/>
 Number of clicks <span data-bind="text:count" ></span>

Sometime you may have to pass current item in click binding. Assume a requirement that you have done foreach binding on element with observable array. Now on click of a particular item, you need to pass that item in ViewModel function to work with that. That can be solved as below. As you see in below ViewModel,

  • There is an observable array. This will be bind to ul or tr element on View
  • There is a function. It takes input which will be used to delete item from observable array.

function viewModel()
 {
 var self = this ;
 self.subject= ko.observableArray(["Phy", "Che", "Bio"]),
 self.removeSubject= function (item)
 {

 self.subject.remove(item);
 }
 }


ko.applyBindings(new viewModel());

On the View we need to do click binding and pass current item to function on which click binding is getting performed.


<ul data-bind="foreach: subject">
 <li>
 <span data-bind="text: $data"></span>
 <button data-bind="click: $parent.removeSubject">Remove</button>
 </li>
</ul>


If you notice we are passing current item using current binding context. So to pass current we are using parent.

In some scenario you will have to pass event object. Event object can be passed as given below,


function viewModel()
 {
 var self = this ;
 self.subject= ko.observableArray(["Phy", "Che", "Bio"]),
 self.removeSubject= function (data,event)
 {
 if (event.shiftKey) {
 self.subject.remove(data);
 }
 }
 }


On View binding can be done as below


<ul data-bind="foreach: subject">
 <li>
 <span data-bind="text: $data"></span>
 <button data-bind="click: $parent.removeSubject($data,event)">Remove</button>
 </li>
</ul>

&nbsp;

Now an item would be deleted on pressing shift key . Understanding of click binding is very useful to create apps using KO. I hope you find this post useful. Thanks for reading.

Control Flow bindings in Knockout.js

You can control flow of element on the view using Control Flow bindings of Knockout.js. There are four Control Flow bindings

  • foreach binding
  • if binding
  • ifnot binding
  • with binding

Out of these four bindings foreach and if bindings are used very often.

foreach binding

foreach binding repeat same markup for number of times equivalent to number of elements in an array. It also binds each repeat of mark-up to corresponding item of the array.

image

Let us try to understand it with an example. Assume we have a ViewModel which contains observable array as one of the observable.


var viewModel =
 {
 students: ko.observableArray
 ([
 {name :"Dan W", subject : "JavaScript" },
 { name: "Glenn B", subject: "Node" },
 { name: "Pinal D", subject: "SQL" }
 ])
 }

ViewModel contains a property students which is an observable array. We can bind this on view to a table or lists elements.


<table>
 <thead>
 <tr>
 <th>Name</th>
 <th>Subject</th>
 </tr>
 </thead>
 <tbody data-bind="foreach: students">
 <tr>
 <td data-bind="text: name"></td>
 <td data-bind="text: subject"></td>
 </tr>
 </tbody>
 </table>

As you notice that on the table body we are performing foreach binding. So mark-up inside table body would be repeated 3 times in this case. Since number of elements in observable array is 3.

On running this application you will get table rendered with three rows as below,

image

Next let us consider one complex scenario in which we want to add and remove students. So let us go ahead an update view model with computed observable to add and remove items from observable array


function viewModel()
 {
 var self=this;
 self.students= ko.observableArray
 ([
 {name :"Dan W", subject : "JavaScript" },
 { name: "Glenn B", subject: "Node" },
 { name: "Pinal D", subject: "SQL" }
 ]);
 self.addStudent= function ()
 {
 self.students.push({ name: "dj", subject: "JavaScript" });
 };
 self.removeStudent= function ()
 {
 self.students.remove(this);
 }
 }

ko.applyBindings(new viewModel());

There is nothing much fancy in above ViewModel. We just added computed observables to add and remove students. Next we can bind that to a view contains unordered list as below


<h4>Students</h4>
 <ul data-bind="foreach: students">
 <li>Name at position <span data-bind="text: $index"></span>:
 <span data-bind="text: name"></span>
 <a href="#" data-bind="click: $parent.removeStudent">Remove</a>
 </li>
 </ul>
 <button data-bind="click: addStudent">Add</button>

On running application you will find that now you can add and remove student

image

You can use $index, $parent, $data with foreach binding as well. For example

  • You can read index of a particular element using $index.
  • You can read array element using $data. Not a particular element
  • You can read parent viewmodel or other properties of viewmodel using $parent

if binding

if binding allows whether a section of mark-up will appear or not. Unlikely visible binding if binding add or remove markup from the DOM. It performs following tasks,

  • Add or remove markup from the DOM
  • Make sure that data-bind attribute is having applied on the markup

Now let us take if binding in action. Let us say you have ViewModel as below


var viewModel =
 {
 show : ko.observable(false)
 }

ko.applyBindings(viewModel);

On View we can do binding as follows


<label><input type="checkbox" data-bind="checked: show" />Display Div</label>
<div data-bind="if: show">Visible/Hide using if binding </div>

As you notice that we are doing checked binding on input checkbox whereas performing if binding on div. If value of ViewModel property evaluates to non-false value then then div would be visible. Input is toggling value of show property.

Let us take another example in which we will learn that how if binding is useful in case of working with null. As you know if binding add or removes elements on the DOM having their data binding applied on that. Consider following ViewModel


function viewModel() {
 var self = this;
 self.students = ko.observableArray
 ([
 { name: "Dan W", subject: null },
 { name: "Glenn B", subject: { music: 'guitar' } },
 { name: "Pinal D", subject: { music: 'drum' } }
 ]);

};

Next we need to do if binding on the view,


<h4>Students</h4>
 <ul data-bind="foreach: students">
 <li>
 Student: <b data-bind="text: name"> </b>
 <div data-bind="if: subject">
 Subject: <b data-bind="text: subject.music"> </b>
 </div>
 </li>
</ul>

Above you see that we are applying if binding on div going to display subject. If binding add or remove on basis of non-falsie value. So for student subject value is set to null this div will not get created. If you try to achieve it using visible binding then for any student for which value if subject is set to null you will get subject.music throwing exception.

Ifnot binding

This is exactly the same as if binding. it only inverts the value passed to it.

So let us say you have ViewModel as below,


var viewModel =
 {
 display : ko.observable(false)
 }

and you bind this to a view as given below,


<div data-bind="ifnot: display">
 <h1>hello</h1>
 </div>

On running you will find div is added on the DOM even though display property in ViewModel is set to false. Because ifnot binding invert value of property in ViewModel.

When you create MVVM application using Knockout.js , control flow binding is very useful. You will use it very often and I hope you find this post useful in understanding control flow binding. Thanks for reading.

Computed Observable in Knockout.js

In this post we will take a look on Computed Observable in Knockout.js. Let us start with an example, you have a ViewModel


var viewModel =
 {
 name: ko.observable("G Block"),
 age: ko.observable(40)
 };

Now you want a custom message to be rendered on the view, that message will depend on other observable of ViewModel. To create that custom message you need to create a Computed Observable. Computed Observable can be created as below,

image

It takes two required parameter,

  • First parameter as function. This function will work with other observable and calculate computed observable
  • Second parameter is name of the function. If you are creating computed observable inside ViewModel then second parameter should be this

So ViewModel with computed observable is as follows,


var viewModel =
 {

name: ko.observable("G Block"),
 age: ko.observable(40),

 };

viewModel.greet = ko.computed(function () {
 return this.name()
 + " is " +
 this.age() +
 " years old";
 }, viewModel);

 ko.applyBindings(viewModel);

On View binding can be done as follows,

 


<input type="text" data-bind="value:name" />
 <input type="text" data-bind="value:age" /> <br />
 <span data-bind="text:greet" />

Few points worth knowing about computed observable

  • It gets evaluated as soon as the computed observable is created. So we may get into problem in a scenario in which computed observable is based on properties which not yet computed or initialized or loaded.
  • If computed observable is based on simple property (not observable property) then change in property will not trigger change in computed observable
  • Change in computed observable triggers if any of the observable property it is based on gets changed.

Computed observable can be created in one more ways as well. You can create ViewModel as below,


function viewModel() {
 var self = this;
 self.name = ko.observable("G Block");
 self.age = ko.observable(40);
 self.greet = ko.computed(function () {
 return self.name()
 + " is " +
 self.age() +
 " years old";
 }, this);
 };

As you notice that we have added computed observable as part of ViewModel. If you follow this way of creating ViewModel then you need to manage this explicitly. We are passing second parameter as this. Without passing it we can’t evaluate computed observable and cannot access other observable to calculate computed observable. So if you don’t pass this as second parameter, you won’t able to access this.age() and this.name(). In above scenario we are preserving this in self inside ViewModel and using self instead of this.

Writeable Computed Observable

So far we have created computed observable reading values of other observables and then returning value based of them. These observables are Read-Only Computed Observable.

You can create writeable Computed Observable as well. You wish to create writeable computed observable in following possible scenarios

  • Interpreting user input
  • Validating user input
  • Setting values of other observables on based on some logic

So let us see how we can use writeable computed observable. Let us say you have a view as below,

clip_image001

Requirements are,

  • When you change run Average should get updated.
  • Average is computed observable and based on Runs and Match

We can achieve above requirement by creating a simple ViewModel and a read only computed observable.

One of other requirement is that when you change Average, Runs should get updated. This can be done by creating a writeable observable.

Let us start with creating a ViewModel,


var playerViewModel =
 {
 name: ko.observable("Sachin T"),
 runs: ko.observable(10000),
 match: ko.observable(100)
 };

Now we need to create a writeable computed observable.


playerViewModel.average = ko.computed({
 read: function ()
 {
 var avg = this.runs() / this.match();
 return avg;
 },
 write: function (value)
 {
 var r = value * this.match();
 this.runs(r);
 },
 owner: playerViewModel
 });

To create writeable computed observable

  • You need to pass JavaScript object to ko.computed function
  • That takes options like read, write, owner etc.
  • Read property takes a function and return value based on other observable
  • Write property also take a function as input. This function take an input parameter “value”. You may wish to perform operation on value and write back resulted value to other observable.
  • You need to ViewModel as owner. This defines the reference of the key

image

In this way you can create writeable computed observable. You can bind values to View as below,


<span data-bind="text:name"></span> <br />
 Runs: <input type="text" data-bind="value:runs" /> <br />
 Match: <input type="text" data-bind="value:match" /> <br />
 Average: <input type="text" data-bind="value:average" />

Deferred Evaluation of computed observable

Before we end this post let us discuss one more concept of deferred execution of computed observable. As we discussed in beginning of this post that computed observable get evaluated at time of creation. So if it is based on observable which is not loaded at time of creation of computed observable then you might run into problem. KO gives you option to deferred execution of computed observable. You can do that by setting deferEvaluation value to true

image

We learnt in this post about Computed Observable. I hope you find this post useful. Thanks for reading.

Why we need Observable Properties in Knockout.js

In this post we will take a look on why we need observable properties in Knockout.js

Before you read further this blog post, I would recommend you to read

Setting up Visual Studio for Knockoutjs Development

Create your First MVVM based JavaScript App in two simple steps using Knockoutjs

Inbuilt Bindings in Knockout.js: Controlling Text and Appearance

MVVM is, “When View Model changes View must be notified such that UI will be refreshed automatically

image

Let us try to understand Knockout feature Automatic UI Refresh with an example,

Let us create a simple ViewModel


var viewModel =
 {
 message: "Hello World",

 };

Now create a simple View. In View we are performing value and text binding on input and span element respectively.


<input type="text" data-bind="value:message" /> <br />
 <span data-bind="text:message" /> <br />

Now on running application, you will notice that when you change value in input textbox that value is not getting refreshed automatically on view.

image

To achieve automatic UI refresh, you need to create ViewModel properties as observable properties. You can create that as following,


var viewModel =
 {
 message: ko.observable("Hello World")

 };

Essentially you need to convert normal properties of ViewModel as observable properties to achieve automatic UI refresh.

image

Now on running application you will find that UI is getting refreshed automatically. You will notice that when you change value in input textbox that value is getting refreshed automatically on view.

clip_image001

Now these are the main tasks performed by observable properties

  • Notify about changes to subscribers
  • Automatically detect dependencies
  • To update view automatically

Reading ViewModel properties

You can read ViewModel property as below,


var m = viewModel.message();
 console.log(m);

Writing ViewModel properties

You can write ViewModel property as below,


viewModel.message("whatsup")

Writing multiple observable properties

KO allow you to write multiple observable property with chained syntax. Let us say we have a ViewModel as below,


var viewModel =
 {
 message: ko.observable("Hello World"),
 greet : ko.observable(23)
 };

And you want to update both properties. You can do that using chained syntax as below,

viewModel.message("whatsup").greet(45);
 var m = viewModel.message();
 var n = viewModel.greet();
 console.log(m);
 console.log(n);

So essentially we need observable properties in KO to achieve,

  • Two way binding
  • Notify the subscribers
  • Automatic refresh UI

I hope you find this post useful. Thanks for reading.