KnockoutJS is build upon the following 3 important concepts.
Observables and dependency tracking between them - DOM elements are connected to ViewModel via 'data-bind'. They exchange information through Observables. This automatically takes care of dependency tracking.
Declarative Bindings between UI and ViewModel - DOM elements are connected to ViewModel via 'data-bind' concept.
Templating to create re-usable components - Templating provides a robust way to create complex web applications.
We will study Observables in this chapter.
As the name specifies, when you declare a ViewModel data/property as Observable, any data modification each time automatically gets reflected at all places the data is used. This also includes refreshing the related dependencies. KO takes care of these things and there is no need to write extra code to achieve this.
Using Observable, it becomes very easy to make UI and ViewModel communicate dynamically.
You just need to declare ViewModel property with function ko.observable() to make it Observable.
this.property = ko.observable('value');
Let's take a look at the following example which demonstrates the use of Observable.
<!DOCTYPE html> <head> <title>KnockoutJS Observable Example</title> <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" type = "text/javascript"></script> </head> <body> <!-- This is called "view" of HTML markup that defines the appearance of UI --> <p>Enter your name: <input data-bind = "value: yourName" /></p> <p>Hi <strong data-bind = "text: yourName"></strong> Good Morning!!!</p> <script> <!-- This is called "viewmodel". This javascript section defines the data and behavior of UI --> function AppViewModel() { this.yourName = ko.observable(""); } // Activates knockout.js ko.applyBindings(new AppViewModel()); </script> </body> </html>
The following line is for the input box. As can be seen, we have used data-bind attribute to bind yourName value to ViewModel.
<p>Enter your name: <input data-bind = "value: yourName" /> <p>
The following line just prints the value of yourName. Note, that here data-bind type is the text as we are simply reading the value.
<p>Hi <strong data-bind = "text: yourName"></strong> Good Morning!!!</p>
In the following line, ko.observable keeps an eye on yourName variable for any modification in data. Once there is a modification, the corresponding places also get updated with the modified value. When you run the following code, an input box will appear. As and when you update that input box, the new value will get reflected or refreshed in places wherever it is used.
this.yourName = ko.observable("");
Let's carry out the following steps to see how the above code works −
Save the above code in first_observable_pgm.htm file.
Open this HTML file in a browser.
Enter the name as Scott and observe that the name is reflected in the output.
Data modification can take place either from the UI or from ViewModel. Irrespective of from where the data is changed, the UI and ViewModel keeps synchronization among them. This makes it a two-way-binding mechanism. In the above example, when you change your name in the input box, ViewModel gets a new value. When you change yourName property from inside ViewModel, then the UI receives a new value.
Following table lists the read and write operations which can be performed on Observables.
Sr.No. | Read/Write Operation & Syntax |
---|---|
1 | Read To read value just call Observable property without parameters like: AppViewModel.yourName(); |
2 | Write To write/update value in Observable property, just pass the desired value in parameter like: AppViewModel.yourName('Bob'); |
3 | Write multiple Multiple ViewModel properties can be updated in a single row with the help of chaining-syntax like: AppViewModel.yourName('Bob').yourAge(45); |
Observable declaration takes care of data modifications of a single object. ObservableArray works with the collection of objects. This is a very useful feature when you are dealing with complex applications containing multiple type of values and changing their status frequently based on the user actions.
this.arrayName = ko.observableArray(); // It's an empty array
Observable array only tracks which objects in it are added or removed. It does not notify if the individual object's properties are modified.
You can initialize your array and at the same time you can declare it as Observable by passing the initial values to the constructor as follows.
this.arrayName = ko.observableArray(['scott','jack']);
You can access Observable array elements as follows.
alert('The second element is ' + arrayName()[1]);
KnockoutJS has its own set of Observable array functions. They are convenient because −
These functions work on all browsers.
These functions will take care of dependency tracking automatically.
Syntax is easy to use. For example, to insert an element into an array, you just need to use arrayName.push('value') instead of arrayName().push('value').
Following is the list of various Observable Array methods.
Sr.No. | Methods & Description |
---|---|
1 | push('value')
Inserts a new item at the end of array. |
2 | pop()
Removes the last item from the array and returns it. |
3 | unshift('value')
Inserts a new value at the beginning of the array. |
4 | shift()
Removes the first item from the array and returns it. |
5 | reverse()
Reverses the order of the array. |
6 | sort()
Sorts array items in an ascending order. |
7 | splice(start-index,end-index)
Accepts 2 parameters - start-index and end-index - removes items starting from start to end index and returns them as an array. |
8 | indexOf('value')
This function returns the index of the first occurrence of parameter provided. |
9 | slice(start-index,end-index)
This method slices out a piece of an array. Returns the items from start-index up to end-index. |
10 | removeAll()
Removes all items and returns them as an array. |
11 | remove('value')
Removes items that match the parameter and returns as an array. |
12 | remove(function(item) { condition })
Removes items which are satisfying the condition and returns them as an array. |
13 | remove([set of values])
Removes items that match with a given set of values. |
14 | destroyAll() Marks all items in an array with property _destroy with value true. |
15 | destroy('value') Searches for an item equal to the parameter and mark it with a special property _destroy with value true. |
16 | destroy(function(item) { condition}) Finds all items which are satisfying the condition, marks them with property _destroy with true value. |
17 | destroy([set of values]) Finds the items that match with a given set of values, marks them as _destroy with true value. |
Note − Destroy and DestroyAll Functions from ObservableArrays are mostly for 'Ruby on Rails' developers only.
When you use destroy method, the corresponding items are not really deleted from array at that moment but are made hidden by marking them with property _destroy with true value so that they can't be read by UI. Items marked as _destroy equal to true are deleted later while dealing with JSON object graph.