Understanding Knockout Binding Context Variable

07 sep. 2022
Intermediate
34,2K Views
7 min read  

Basically, binding context is an object that holds data, which is referenced from your view-model bindings. While applying bindings, Knockout automatically creates and manages a hierarchy of binding contexts. Let's have a look on different types of binding context with example.

The $root Property

The $root context always refers to the top-level ViewModel, regardless of loops or other changes in scope. This allow us to access top-level methods for manipulating the ViewModel.

For example, inside the foreach: shoppingCart loop, $root refers to top-level methods removeProduct of ViewModel as shown below:

<tbody data-bind='foreach: shoppingCart'>
 <tr>
 .
 . 
 <td>
<button data-bind='click: $root.removeProduct'>Remove</button>
</td>
</tr>
</tbody>

The $data Property

The $data binding context refers to the ViewModel object in the current context. It is much more like the this keyword in a JavaScript object.

For example, inside the foreach: shoppingCart loop, $data refers to the current list item as shown below:

<tr data-bind='foreach: shoppingCart'>
<td data-bind='text: $data.name'></td>
<td data-bind='text: $data.price'></td>
</tr>

The $index Property

Inside of a foreach loop, the $index property contains the current item’s index in the array. Unlike the other binding context properties, $index is an observable and is updated automatically whenever you add or delete an item from the associated observable array.

<tr data-bind='foreach: shoppingCart'>
<td data-bind='text: $index'></td>
<td data-bind='text: $data.name'></td>
<td data-bind='text: $data.price'></td>
</tr>

The $parent Property

The $parent property context refers to the parent ViewModel object. Typically, you will require this prroperty when you will work with nested loops and with in the nested loop, you need to access properties of the outer loop.

For example, if you need to access the Product instance from the inside of the foreach: types loop, you could use the $parent property as shown below:

<tbody data-bind='foreach: shoppingCart'>
<tr>
.
.
<td>
<ul data-bind="foreach: types">
<li>
<span data-bind="text: $parent.name"></span> -
<span data-bind="text: $data"></span>
</li>
</ul>
</td>
.
.
</tbody data-bind='foreach: shoppingCart'>
</tr>

$parentContext

This refers to the binding context object at the parent level. This is different from $parent, which refers to the data (not binding context) at the parent level.

For example, if you need to access the index value of the outer foreach item with in inner foreach context you could use the $parentContext as shown below:

<tbody data-bind='foreach: shoppingCart'>
<tr>
.
.
<td>
<ul data-bind="foreach: types">
<li>
<span data-bind="text: $parentContext.$index"></span><span data-bind="text: $parent.name"></span> -
<span data-bind="text: $data"></span>
</li>
</ul>
</td>
.
.
</tbody data-bind='foreach: shoppingCart'>
</tr>

$parents

This is an array that represent all of the parent view models. This is useful when you need to access the parents with in inner most loops that may nested upto n-level.

$parents[0] is the view model from the parent context (i.e., it’s the same as $parent)

$parents[1] is the view model from the grandparent context

$parents[2] is the view model from the great-grandparent context

…

and so on.

Note

All the binding context properties are only available in the view, not in the ViewModel.

Example of Knockout Binding Context ($root,$data,$parent,$index)

<html lang='en'>
<head>
 <title>Knockout Binding Context</title>
 <style type="text/css">
 body
{
 margin: 20px;
 font-family: "Arial" , "Helventica" , sans-serif;
}

button
{
 display: inline-block;
 outline: none;
 cursor: pointer;
 text-align: center;
 text-decoration: none;
 padding: .4em 1.1em .4em;
 color: #fef4e9;
 border: solid 1px #006fb9;
 background: #1276bb;
 
}
button:hover
{
 text-decoration: none;
 background: #282828;
 border: solid 1px #000;
 }

table
{
 padding-top: 1em;
}

thead ,tfoot{
font-weight:600;

}
th, td
{
 padding: .1em .5em;
 text-align: left;
}

td li, td ul
{
 margin: 0;
 padding: 0;
}

td li
{
 display: inline;
}

td li::after
{
 content: ',';
}

td li:last-child::after
{
 content: '';
}

 </style>
</head>
<body>
 <h2>Knockout Binding Context</h2>
 <p><span data-bind="text:fullName"></span>'s Order</p>
 
 <button data-bind='click:addProduct'>Add Shampoo</button>
 <button data-bind='click:checkout'>Check Out</button>

 <table>
 <thead><tr>
 <th>Item number</th>
 <th>Product</th>
 <th>Price</th>
 <th>Types</th>
 <th>Action</th>
 </tr></thead>
 <tbody data-bind='foreach: shoppingCart'>
 <tr>
 <td data-bind='text: $index()+1'></td>
 <td data-bind='text: name'></td>
 <td data-bind='text: price'></td>
 <td>
 <ul data-bind="foreach: types">
 <li>
 <span data-bind="text: $parent.name"></span> - <span data-bind="text: $data"></span>
 </li>
 </ul>
 </td>
 
 <td>
 <button data-bind='click: $root.removeProduct'>Remove</button>
 </td>
 </tr>
 </tbody>
 <tfoot>
 <tr>
 <td></td>
 <td>Total :</td>
 <td data-bind="text: $root.total()"></td>
 <td></td>
 <td></th>
 </tr>
 </tfoot>
 </table>
 
 <script type='text/javascript' src='knockout-2.1.0.js'></script>
 <script type='text/javascript'>
 function Product(name, price, types) {
 var self = this;
 
 self.name = ko.observable(name);
 self.price = ko.observable(price);
 types = typeof (types) !== "undefined" ? types : []; //if empty then null
 self.types = ko.observableArray(types);
 }

 function PersonViewModel() 
 {
 
 var self = this;

 self.firstName = ko.observable("Saksham");
 self.lastName = ko.observable("Chauhan");
 self.fullName = ko.computed(function(){
 return self.firstName() + " " + self.lastName();
 }, this);

 self.shoppingCart = ko.observableArray([
 new Product("Soap", 10.99, ["Soft", "Hard"]),
 new Product("Shampoo", 7.99),
 new Product("Pickle", 15.49, ["Spicy", "Sweet"])
 ]);

 self.total = ko.computed(function(){
 var sum=0;
 var arr = self.shoppingCart();
 for (var i = 0; i < arr.length; i++) {
 sum += arr[i].price() ;
 }
 return sum;
 });
 
 self.addProduct = function () {
 self.shoppingCart.push(new Product("More Shampoo", 7.99));
 };

 self.removeProduct = function (product) {
 if(confirm('Are you sure to delete?'))
 {
 self.shoppingCart.remove(product);
 }
 
 };

 self.checkout = function () {
 var items = "";
 var arr = self.shoppingCart();
 for (var i = 0; i < arr.length; i++) {
 items += arr[i].name() + "\n"; // don't forget that you make a function call when accessing observable properties
 }
 alert(items);
 };
 
 };

 var vm = new PersonViewModel();
 ko.applyBindings(vm);
 </script>

</body>
</html>

Output

Adding Product to Order List

Remove Product from Order List

Checkout Order List

What do you think?

I hope you will enjoy the tips while working with Knockout. I would like to have feedback from my blog readers. Your valuable feedback, question, or comments about this article are always welcome.

Share Article
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 8th time in a row (2016-2023). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Learn to Crack Your Technical Interview

Accept cookies & close this