Introduction
- Very often, you'll want to generate repeating blocks of UI elements, especially when displaying lists where the user can add and remove elements
- Knockout lets you do that easily, using observable Arrays and the
foreach
binding
What is Observable Arrays ?
- If you want to detect and respond to changes on one object, you’d use
observables
- If you want to detect and respond to changes of a collection of things, use an
observableArray
- This is useful in many scenarios where you’re displaying or editing multiple values and need repeated sections of
UI to appear and disappear as items are added and removed
e.g.
var myObservableArray = ko.observableArray();
myObservableArray.push('Some value');
Important Note
- An
observableArray
tracks which objects are in the array, not the state of those objects - Simply putting an object into an
observableArray
doesn’t make all of that object’s properties themselves
observable - Of course, you can make those properties observable if you wish, but that’s an independent choice
- An
observableArray
just tracks which objects it holds,
and notifies listeners when objects are added or removed
How to Apply observableArray with Real world Application ?
- Here I have used Visual Studio 2012 and ASP.NET MVC 4
Web Application
- Please Follow the inline comments for better understanding
CASE 1 : Displaying Reservation Data
View's Code - Index.cshtml
<h2>Your Seat Reservations</h2>
<table>
<thead>
<tr>
<th>Passenger Name</th>
<th>Meal</th>
<th>Amount ($)</th>
<th></th>
</tr>
</thead>
@*render a copy of seats child elements for each entry in the seats array*@
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: meal().mealName"></td>
<td data-bind="text: meal().price"></td>
</tr>
</tbody>
</table>
ViewModel (Javascript) Code - ko.list.js
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal);
}
function ReservationsViewModel() {
var self = this;
self.availableMeals = [
{ mealName: "Vegetarian Raw Meal", price: 10.52 },
{ mealName: "Vegetarian Vegan Meal", price: 34.95 },
{ mealName: "Fruit Platter Meal", price: 45.50 }
];
self.seats = ko.observableArray([
new SeatReservation("Sampath", self.availableMeals[0]),
new SeatReservation("Lokuge", self.availableMeals[1])
]);
}
ko.applyBindings(new ReservationsViewModel());
Output
Important Points about above Code
SeatReservation
- a simple JavaScript class constructor that
stores a passenger name with a meal selection
ReservationsViewModel
, a
ViewModel class
availableMeals
- a JavaScript
object providing meal data seats
- an
array holding an initial collection of
SeatReservation instances.Note that it's a
ko.observableArray
,
which means it can automatically trigger UI updates whenever items are added or removed
meal
Property
- is an observable
- It's important to invoke
meal()
as a function (to obtain its current value)
before attempting to read sub-properties - In other words, write
meal().price
, not
meal.price
CASE 2 : Adding items
View's Code - Index.cshtml
@*... leave all the rest unchanged ...*@
<button data-bind="click: addSeat">Reserve Another Seat</button>
ViewModel (Javascript) Code - ko.list.js
function ReservationsViewModel() {
var self = this;
self.addSeat = function () {
self.seats.push(new SeatReservation("Chaminda", self.availableMeals[2]));
};
}
Output

Explanation about above Scenario
- Now when you click "Reserve Another Seat", the
UI updates to match
- This is because seats is an
observable Array, so adding or removing items
will trigger UI updates automatically
- Note that adding a row does not involve regenerating the entire UI
- For efficiency, Knockout tracks what has changed in your
ViewModel, and performs a minimal set of DOM updates to match
CASE 3 : Edit items
View's Code - Index.cshtml
@*... leave all the rest unchanged ...*@
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: name"></td>
<td><select data-bind="options: $root.availableMeals, value: meal,
optionsText: 'mealName'"></select></td>
<td data-bind="text: meal().price"></td>
</tr>
</tbody>
Output
Important Points about above Code
- This code uses two new bindings, options and
optionsText
- Which together control both the set of available items in a dropdown list, and
which object property (in this case,
mealName
) is used to represent each item on screen - You can now select from the available meals, and
doing so causes the corresponding row (only) to be refreshed to display that
meal's price
CASE 4 : Formatting Prices
View's Code - Index.cshtml
@*... leave all the rest unchanged ...*@
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: name"></td>
@*update the view to make use of the formattedPrice*@
<td><select data-bind="options: $root.availableMeals, value: meal,
optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
</tr>
</tbody>
ViewModel (Javascript) Code - ko.list.js
function SeatReservation(name, initialMeal) {
var self = this;
self.formattedPrice = ko.computed(function () {
var price = self.meal().price;
return price ? "$" + price.toFixed(2) : "None";
});
}
Output

Important Points about above Code
- We've got a nice object-oriented representation of our data
- So we can trivially add extra properties and functions anywhere in the object graph
- The
SeatReservation
class the ability to
format its own price data using
some custom logic - Since the formatted price will be
computed based on the selected meal, we can represent it using
ko.computed (so it will update automatically whenever the meal selection changes)
CASE 5 : Removing Items
View's Code - Index.cshtml
<tbody data-bind="foreach: seats">
<tr>
@*... leave all the rest unchanged ...*@
<td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
</tr>
</tbody>
ViewModel (Javascript) Code - ko.list.js
function ReservationsViewModel() {
var self = this;
self.removeSeat = function (seat) { self.seats.remove(seat); };
}
Output

Important Points about above Code
- Note that the
$root
. prefix causes
Knockout to look for a removeSeat
handler on your
top-level ViewModel, instead of on the
SeatReservation
instance being bound - That's a more convenient place to put
removeSeat
in this example - So,I have added a corresponding
removeSeat
function on root ViewModel
class, That is
ReservationsViewModel
CASE 6 : Displaying a Total Amount
View's Code - Index.cshtml
@*... leave all the rest unchanged ...*@
<h3 data-bind="visible: totalAmount() > 0">Total Amount: $
<span data-bind="text: totalAmount().toFixed(2)"></span>
</h3>
ViewModel (Javascript) Code - ko.list.js
function ReservationsViewModel() {
var self = this;
self.totalAmount = ko.computed(function () {
var total = 0;
for (var i = 0; i < self.seats().length; i++)
total += self.seats()[i].meal().price;
return total;
});
}
Output

Important Points about above Code
- I have defined the total as a computed property
- It lets the framework, take care of knowing when to recalculate and refresh the display
- The visible binding makes an element visible or invisible as your data changes (internally, it modifies the element's
CSS display style)
- In this case, we choose to show the "Total Amount" information
only if it's greater than zero
- You can use arbitrary JavaScript expressions inside
declarative bindings
- Here, we used
totalAmount()
> 0 and
totalAmount().toFixed(2)
- Internally, this actually defines a computed property to represent the
output from that expression
- It's just a very lightweight and convenient syntactical alternative
- Again, notice that since seats and meal are both observables, we're invoking them as functions to read their
current values (e.g.
self.seats().length
) - When you run the code, you'll see "Total Amount" appear and disappear as appropriate, and
thanks to dependency tracking, it knows when to recalculate its own value
- You don't need to put any code in your "add" or "remove" functions to force dependencies to update manually
CASE 7 : Display the Total Number of Seats being Reserved
View's Code - Index.cshtml
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>
@*... leave all the rest unchanged ...*@
<button data-bind="click: addSeat, enable: seats().length < 3">Reserve Another Seat</button>
Output

Important Points about above Code
- For display the Total number of seats being reserved, you can implement that in just a single place
- You don't have to write any extra code to make the seat count update when you add or remove items
- Just update the
<h2>
as above on top of your
View - Similarly, For a Limit on the number of seats you can reserve
- You can make the UI represent that by using the enable binding
- The button becomes disabled when the seat limit is reached
- You don't have to write any code to re-enable it, when the user removes some seats
- Because the expression will
automatically be re-evaluated by
Knockout when the associated data changes
Final Full Code
Index.cshtml
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>
<table>
<thead>
<tr>
<th>Passenger Name</th>
<th>Meal</th>
<th>Amount ($)</th>
<th></th>
</tr>
</thead>
@*render a copy of seats child elements for each entry in the seats array*@
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: name"></td>
@*update the view to make use of the formatted Price*@
<td>
<select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
<td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
</tr>
</tbody>
</table>
<button data-bind="click: addSeat, enable: seats().length < 3">Reserve Another Seat</button>
<h3 data-bind="visible: totalAmount() > 0">Total Amount: $<span data-bind="text: totalAmount().toFixed(2)"></span></h3>
ko.list.js
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal);
self.formattedPrice = ko.computed(function () {
var price = self.meal().price;
return price ? "$" + price.toFixed(2) : "None";
});
}
function ReservationsViewModel() {
var self = this;
self.availableMeals = [
{ mealName: "Vegetarian Raw Meal", price: 10.52 },
{ mealName: "Vegetarian Vegan Meal", price: 34.95 },
{ mealName: "Fruit Platter Meal", price: 45.50 }
];
self.seats = ko.observableArray([
new SeatReservation("Sampath", self.availableMeals[0]),
new SeatReservation("Lokuge", self.availableMeals[1])
]);
self.totalAmount = ko.computed(function () {
var total = 0;
for (var i = 0; i < self.seats().length; i++)
total += self.seats()[i].meal().price;
return total;
});
self.addSeat = function () {
self.seats.push(new SeatReservation("Chaminda", self.availableMeals[2]));
};
self.removeSeat = function (seat) { self.seats.remove(seat); };
}
ko.applyBindings(new ReservationsViewModel());
That's It.You're Done.
Conclusion
- You saw that following the MVVM pattern makes it very simple to work withchangeable object graphs such as Arrays and Hierarchies
- You update the underlying data, and the UI automatically updates in sync
- So enjoy with this Great Framework