- KnockoutJS Tutorial
- KnockoutJS - Home
- KnockoutJS - Overview
- KnockoutJS - Environment Setup
- KnockoutJS - Application
- KnockoutJS - MVVM Framework
- KnockoutJS - Observables
- Computed Observables
- KnockoutJS - Declarative Bindings
- KnockoutJS - Dependency Tracking
- KnockoutJS - Templating
- KnockoutJS - Components

- KnockoutJS Resources
- KnockoutJS - Quick Guide
- KnockoutJS - Resources
- KnockoutJS - Discussion

Computed Observable is a function which is dependent on one or more Observables and automatically updates whenever its underlying Observables (dependencies) change.

Computed Observables can be chained.

this.varName = ko.computed(function(){ ... ... // function code ... },this);

Let us look at the following example which demonstrates the use of Computed Observables.

<!DOCTYPE html> <head > <title>KnockoutJS Computed Observables</title> <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"></script> </head> <body> <p>Enter first number: <input data-bind = "value: a" /></p> <p>Enter second number: <input data-bind = "value: b"/></p> <p>Average := <span data-bind="text: totalAvg"></span></p> <script> function MyViewModel() { this.a = ko.observable(10); this.b = ko.observable(40); this.totalAvg = ko.computed(function() { if(typeof(this.a()) !== "number" || typeof(this.b()) !== "number") { this.a(Number(this.a())); //convert string to Number this.b(Number(this.b())); //convert string to Number } total = (this.a() + this.b())/2 ; return total; },this); } ko.applyBindings(new MyViewModel()); </script> </body> </html>

In the following lines, first two are for accepting input values. Third line prints the average of these two numbers.

<p>Enter first number: <input data-bind = "value: a" /></p> <p>Enter second number: <input data-bind = "value: b"/></p> <p>Average := <span data-bind = "text: totalAvg"></span></p>

In the following lines, type of Observables **a** and **b** is number when they are initialized for the first time inside ViewModel. However, in KO every input accepted from UI is by default in the String format. So they need to be converted to Number so as to perform arithmetic operation on them.

this.totalAvg = ko.computed(function() { if(typeof(this.a()) !== "number" || typeof(this.b()) !== "number") { this.a(Number(this.a())); //convert string to Number this.b(Number(this.b())); //convert string to Number } total = (this.a() + this.b())/2 ; return total; },this);

In the following line, the calculated average is displayed in the UI. Note that data-bind type of totalAvg is just text.

<p>Average := <span data-bind = "text: totalAvg"></span></p>

Let's carry out the following steps to see how the above code works −

Save the above code in

**computed-observable.htm**file.Open this HTML file in a browser.

Enter any 2 numbers in the text boxes and observe that the average is calculated.

Note that in the above example, the second parameter is provided as **this** to Computed function. It is not possible to refer to Observables **a()** and **b()** without providing **this**.

To overcome this, **self** variable is used which holds the reference of **this**. Doing so, there is no need to track **this** throughout the code. Instead, **self** can be used.

Following ViewModel code is rewritten for the above example using self.

function MyViewModel(){ self = this; self.a = ko.observable(10); self.b = ko.observable(40); this.totalAvg = ko.computed(function() { if(typeof(self.a()) !== "number" || typeof(self.b()) !== "number") { self.a(Number(self.a())); //convert string to Number self.b(Number(self.b())); //convert string to Number } total = (self.a() + self.b())/2 ; return total; }); }

A Computed Observable should be declared as **Pure** Computed Observable if that Observable is simply calculating and returning the value and not directly modifying the other objects or state. Pure Computed Observables helps Knockout to manage reevaluation and memory usage efficiently.

When a Computed Observable is returning primitive data type value (String, Boolean, Null, and Number) then its subscribers are notified if and only if the actual value change takes place. It means if an Observable has received the value same as the previous value, then its subscribers are not notified.

You can make Computed Observables always explicitly notify the observers, even though the new value is the same as the old by using the **notify** syntax as follows.

myViewModel.property = ko.pureComputed(function() { return ...; // code logic goes here }).extend({ notify: 'always' });

Too many expensive updates can result in performance issues. You can limit the number of notifications to be received from Observable using **rateLimit** attribute as follows.

// make sure there are updates no more than once per 100-millisecond period myViewModel.property.extend({ rateLimit: 100 });

In certain situations, it might be necessary to find out if a property is a Computed Observable. Following functions can be used to identify the types of Observables.

Sr.No. | Function |
---|---|

1 |
Returns |

2 |
Returns |

3 |
Returns |

Computed Observable is derived from one or multiple other Observables, so it is read only. However, it is possible that one can make Computed Observable writable. For this you need to provide callback function that works on written values.

These writable Computed Observables work just like regular Observables. In addition, they require custom logic to be built for interfering read and write actions.

One can assign values to many Observables or Computed Observable properties using the chaining syntax as follows.

myViewModel.fullName('Tom Smith').age(45)

Following example demonstrates the use of Writable Computable Observable.

<!DOCTYPE html> <head > <title>KnockoutJS Writable Computed Observable</title> <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script> </head> <body> <p>Enter your birth Date: <input type = "date" data-bind = "value: rawDate" ></p> <p><span data-bind = "text: yourAge"></span></p> <script> function MyViewModel() { this.yourAge = ko.observable(); today = new Date(); rawDate = ko.observable(); this.rawDate = ko.pureComputed ({ read: function() { return this.yourAge; }, write: function(value) { var b = Date.parse(value); // convert birth date into milliseconds var t = Date.parse(today); // convert todays date into milliseconds diff = t - b; // take difference var y = Math.floor(diff/31449600000); // difference is converted // into years. 31449600000 //milliseconds form a year. var m = Math.floor((diff % 31449600000)/604800000/4.3); // calculating // months. // 604800000 // milliseconds // form a week. this.yourAge("You are " + y + " year(s) " + m +" months old."); }, owner: this }); } ko.applyBindings(new MyViewModel()); </script> </body> </html>

In the above code, **rawDate** is pureComputed property accepted from UI. **yourAge** Observable is derived from **rawDate**.

Dates in JavaScript are manipulated in milliseconds. Hence, both the dates (today date and birth date) are converted into milliseconds and then the difference between them is converted back in years and months.

Let's carry out the following steps to see how the above code works −

Save the above code in

**writable_computed_observable.htm**file.Open this HTML file in a browser.

Enter any birth date and observe that the age is calculated.

Advertisements