In our previous post Dynamic UI Using Observables with MVVM Using Knockout.js, you learned how you get started with Knockout.js and how you can detect and respond to changes on one object using observables.
Knockout.js simplifies JavaScript UI by applying the Model-View-ViewModel pattern.
Now if you want to detect and respond to changes of a collection of things, you can use an observableArray
. An observableArray tracks which objects are in the array, not the state of those objects.
An observableArray
tracks which objects it holds, and notifies listeners when objects are added or removed.
You can make the items themselves observable if you wish, but we’ll start with a basic observableArray.
Let’s start with a simple list of products and make them an observableArray.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This observable array initially contains four objects | |
var productArray = ko.observableArray([ | |
{ product: "Widget", type: "Tool" }, | |
{ product: "Bolt", type: "Fastener" }, | |
{ product: "Washer", type: "Fastener" }, | |
{ product: "Screwdriver", type: "Tool" } | |
]); |
Using Control Flow Bindings
Now that we have an observableArray, we can use the foreach binding to display the data in a table.
The foreach
binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables.
<tbody data-bind="foreach: productArray">
<tr>
<td data-bind="text: product"></td>
<td data-bind="text: type"></td>
</tr>
</tbody>
In the following example, you can see how an observable array is bound to a table using Knockout.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Bind Observable Array to Table</title> | |
</head> | |
<body> | |
<table border="0"> | |
<thead> | |
<tr> | |
<th>Product</th> | |
<th>Type</th> | |
</tr> | |
</thead> | |
<tbody data-bind="foreach: productArray"> | |
<tr> | |
<td data-bind="text: product"></td> | |
<td data-bind="text: type"></td> | |
</tr> | |
</tbody> | |
</table> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script> | |
var productArray = ko.observableArray([ | |
{ product: "Widget", type: "Tool" }, | |
{ product: "Bolt", type: "Fastener" }, | |
{ product: "Washer", type: "Fastener" }, | |
{ product: "Screwdriver", type: "Tool" } | |
]); | |
ko.applyBindings(new productArray()); | |
</script> | |
</body> | |
</html> |
Manipulating the Array
To start with, the most basic thing you can do is display and add to an array. The following sample shows how to bind a list of items to a select box using an observableArray. You can then click the Add button to add items to the view model.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Observable Array 1</title> | |
</head> | |
<body> | |
<form > | |
New item: | |
<button data-bind="click: addItem">Add</button> | |
<select multiple="multiple" data-bind="options: observableItems"></select> | |
</form> | |
<script src="Scripts/knockout-2.2.1.debug.js"></script> | |
<script> | |
var number = 0; | |
var listData = ["Alpha", "Beta", "Gamma"]; | |
var listViewModel = function (items) { | |
this.observableItems = ko.observableArray(items); | |
this.count = this.observableItems().length; | |
this.addItem = function () { | |
this.observableItems.push("Item " + number++); // Adds a generic item. | |
}.bind(this); // Ensure that "this" is always this view model | |
}; | |
ko.applyBindings(new listViewModel(listData)); | |
</script> | |
</body> | |
</html> |
When you add an item to the array, you can push it onto the array using one of the many standard JavaScript array functions.
Standard JavaScript Array Functions
You can use any of the native JavaScript array functions to operate on that underlying array, such as:
alert('The length of the array is ' + productArray().length); alert('The first element is ' + productArray()[0]);
You can also use Knockout functions, such as indexOf that will work across browsers. (The JavaScript method indexOf does not work in IE 8 or earlier. But in Knockout it works everywhere.)
- indexOf function returns the index of the first array item that equals your parameter. For example,
myObservableArray.indexOf('Blah')
will return the zero-based index of the first array entry that equalsBlah
, or the value-1
if no matching value was found. - slice function is the
observableArray
equivalent of the native JavaScriptslice
function. It returns the entries of your array from a given start index up to a given end index. push('Some new value')
adds a new item to the end of array.pop()
removes the last value from the array and returns it.unshift('Some new value')
inserts a new item at the beginning of the array.shift()
removes the first value from the array and returns itreverse()
reverses the order of the array.sort()
sorts the array contents. By default, it sorts alphabetically (for strings) or numerically (for numbers). Optionally, you can pass a function to control how the array should be sorted.splice()
removes and returns a given number of elements starting from a given index. For example,myObservableArray.splice(1, 3)
removes three elements starting from index position 1 (i.e., the 2nd, 3rd, and 4th elements) and returns them as an array.
For more details about these observableArray
functions, see the equivalent documentation of the standard JavaScript array functions.
Helpful Methods Not Found in JavaScript
observableArray
adds some more useful methods that aren’t found on JavaScript arrays by default:
remove(someItem)
removes all values that equalsomeItem
and returns them as an arrayremove(function(item) { return item.age < 18 })
removes all values whoseage
property is less than 18, and returns them as an arrayremoveAll(['Chad', 132, undefined])
removes all values that equal'Chad'
,123
, orundefined
and returns them as an arrayremoveAll()
removes all values and returns them as an array
In addition, destroy
and destroyAll
functions are mainly intended as a convenience for developers using Ruby on Rails.