When I wrote Knockout.js: Building Dynamic Client-Side Web Applications I was trying to focus on demonstrating specific things, such as custom bindings, extending observables, etc. Unfortunately this didn’t leave room for what I would call “random” examples of things that I do on a semi-regular basis. This blog post will demonstrate how to create a (un)check all list of checkboxes.
If I haven’t said it before, Knockout js examples like this are why I love working with this framework on a daily basis. This example is accomplished in under 50 lines of code, with most of it being whitespace for readability!
The following example assumes you understand how to install Knockout.js and have a brief understanding of Knockout ViewModels. If you are looking for a good introduction *cough* *cough*, my book does an excellent job of it 😉
Using the checked data binding with an observable array
You have a list of elements – such as emails or any other list of data – and you want to provide the user a one-click button to select (or unselect) all items in the list. A nice added bonus, you want to automatically update the “global” checkbox indicating when all items are checked (and unchecking it when all items are not checked).
This problem can be solved by leveraging a computed observable. With Knockout.js, computed observables are re-evaluated each time an observable variable changes in the computed function. To avoid a circular reference, a writable computed observable will be used to force the (un)checking of all items.
This example is using Knockout.js version 3.3; however, it should be compatible with older versions as well.
Firstly, be sure you have the Knockout.js library – this example assumes it is in the same folder as the example file, be sure to update it as necessary.
To accomplish the checking/unchecking of all items, two key ingredients are required:
– A checkbox that allows the user to check/uncheck all items
– A list of checkboxes that can be individually selected or automatically selected (unselected) by the global checkbox
Before continuing with any further explanations, let’s look at the finished code:
[code]
<html>
<head></head>
<body>
<form>
<input type=”checkbox” name=”checkall” data-bind=”checked: checkall” />
<ul>
<!– ko foreach: items –>
<li><input type=”checkbox” name=”selectedItems” data-bind=”checked: $parent.selectedItems, attr: { value: value }” /></li>
<!– /ko –>
</ul>
</form>
<script src=”knockout.js”></script>
<script>
function ViewModel() {
var self = this;
self.items = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
{ id: 4, value: 4 },
{ id: 5, value: 5 }
];
self.selectedItems = ko.observableArray();
self.checkall = ko.computed({
read: function() {
return self.items.length == self.selectedItems().length;
},
write: function(newValue) {
self.selectedItems([]);
if (newValue) {
for (var i = 0; i < self.items.length; i++)
self.selectedItems.push(self.items[i].value.toString());
}
}
});
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
</script>
</body>
</html>
[/code]
Inside the HTML body, the first checkbox is defined. It contains a data-binding to the computed observable checkall – more on this in a minute. Next, an unordered list is defined that performs a foreach data-binding on an array of items. The checkbox within each element is data bound to the selectedItems array. As items are selected (unselected), the values will be added and removed from the array. This completes the basic HTML. In your example, this would require additional styling to show more information about each item.
After the HTML comes the JavaScript. The Knockout.js library is included followed by the ViewModel that Knockout will be bound too.
The ViewModel does three things. The first defines the array of items that are applied to the foreach data-binding. Because this list does not change, it is a basic JavaScript array and not an observable array. Next an observable array is defined for the list of items that are checked via the checkboxes (or from the global check all). This is the array you will wish to interact with after the user performs action on the set of selected items, e.g. delete the selected emails.
The final piece of the ViewModel is the computed observable called checkall. This is a writable computed observable that allows the computed value to be forcibly overwritten. Inside this computed observable, two properties are defined. The value when read and the value when written. The global checkbox is data bound to the computed observable. This means that when the length of the items array is equal to the length of the selectedItems observable array, the checkbox will be checked automatically. Like-wise, if it does not match, the checkbox will be unchecked.
When the user selects the global checkbox, the write property is called with either true or false depending on if the checkbox is checked or not. When the write function is called, it clears the selectedItems array and if the global checkbox is checked, the array of items are looped through and the value is added to the selectedItems observable array.
When the user either uses the global check all or manually selects all items, the check all checkbox will be checked. If at any time, all items are not checked, the check all checkbox will remain unchecked.
To make all of this even better you can continuing to my article about converting this entire example into Knockout js component that makes this feature highly re-usable.
For readability the example has been broken into small chunks, the full source code is available on GitHub – Knockout js Examples