Convert the (un)check all to a #KnockoutJS Component

Published on Apr 29, 2015 by Jamie Munro

Problem


You've created a nice feature leveraging Knockout.js and now you want to re-use this feature on another page, another site, or in a slightly different fashion. This example will extend the previous (Un)Check All using #KnockoutJS and make it easily re-usable.

Solution


A Knockout component can be created using a mixture of HTML (with data bindings) and a Knockout ViewModel. By altering the previous check all example and making it slightly more re-usable, it can be easily added to other pages with minimal effort. If you are not already familiar with the example being extended, please take a minute and give the (Un)Check All using #KnockoutJS a quick read.

Once the component is created, it can be included on any page with this simple HTML:
<checkall params="items: items, selectedItems: selectedItems"></checkall>



Discussion


Let's start with the HTML template. The following example takes the exact same HTML that was previously defined and places it inside a template tag with the ID of checkAll.


<template id="checkAll">
<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>
</template>


This is the nice part. This design can be customized for each page that needs to include the check all functionality, just be sure to leave the global checkbox that is data bound to the checkall observable, the foreach of the items array and the collection of checkboxes that are data bound to the selectedItems observable array. Other than these three things, any styling required can be added.

To complete the Knockout component creation, a ViewModel is required. After the definition of the ViewModel, the component must be registered. This example demonstrates this.


<script>
function CheckAllViewModel(params) {
var self = this;

self.items = params.items;
self.selectedItems = params.selectedItems;

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());
}
}
});
};

ko.components.register('checkall', {
viewModel: CheckAllViewModel,
template: { element: 'checkAll' }
});
</script>


The ViewModel for the Knockout component is called CheckAllViewModel. This model accepts a single input called params. The params parameter is an object of the parameters that are passed to the component when it is defined (shown momentarily). The CheckAllViewModel is expecting two parameters, the array of items to loop through and the observable array of checked items. The checkall computed observable is identical to how it was previously defined in the non-component example.

After the ViewModel definition, the checkall Knockout component is registered supplying the name of the ViewModel and the ID of the template Knockout should use. That completes the definition of the component. The final piece is to include the component and a new ViewModel for this page.


<html>
<head></head>
<body>
<checkall params="items: items, selectedItems: selectedItems"></checkall>

<button type="button" data-bind="click: nextClick">Next</button>

<script src="knockout.js"></script>
<script>
function MainViewModel() {
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.nextClick = function() {
alert(self.selectedItems());
};
};

var viewModel = new MainViewModel();
ko.applyBindings(viewModel);
</script>
</body>
</html>


The Knockout component is included like an HTML element using the same name as it was registered with. In the params list, the two arrays are provided from the MainViewModel that is bound to this page. Beneath the component, is a simple button that contains a click data binding to a function called nextClick. This function simply alerts the observable array of selectedItems.

After the HTML, the MainViewModel is defined. It looks quite similar to the ViewModel from the original example in that it defines the array of items to loop through and the observable array of selectedItems. The main difference from the MainViewModel and the original ViewModel is that it doesn't contain the re-usable logic in the computed observable checkall property.

For readability the example has been broken into small chunks, the full source code is available on GitHub.

Tags: KnockoutJS | knockoutjs | component

Related Posts

blog comments powered by Disqus