Building a list of selectable items in Knockout JS can be a bit confusing at first.  Not because it’s hard, but because you have so many ways to do it.  This post is part one of a three-part series where I’ll walk you through three ways to build select lists, and why and when to use each technique.

When you’re designing your Knockout JS-backed select list you’re spoiled for options.  Ultimately the approach you choose will be determined by two things: what you choose for your view, and what you need for your models.  In this post I’ll take you through a simple situation; a single list of selectable items in your model, and checkboxes in your view.

I’m going to walk though this decision by decision, so if you’d like to jump to the final code, feel free to scroll down to the end.

We’ll start with a simple model that has a list of Software as a Service (SaaS) apps.  In order to keep things focused, I’ll make this list a simple array rather than an observable array, but of course the technique will work in either case.

var viewModel = function(){
    var self = this;
    self.apps = [
        'Freckle',
        'Beanstalk',
        'DropBox',
        'Postmark'
    ];
}

Since we love and respect HTML, we’ll keep things semantic and bind this to an unordered list.  For each item in the list, we’ll render out a checkbox inside a label with our item name as the text in the label.

<ul class="unstyled" data-bind="foreach: apps">
    <li>
        <label>
            <input type="checkbox" />
        </label>
    </li>
</ul>

We’ve got our list and our checkboxes, time to hook them up.

Selecting Items

Since the items in our array are only for this select list, a good first approach is to turn your items into objects and add a selected property.  This choice does have some limitations in this instance, which we’ll take a look at in a moment.

var viewModel = function(){
    var self = this;
    self.apps = [
        {name:'Freckle',selected:false},
        {name:'Beanstalk',selected:false},
        {name:'DropBox',selected:false},
        {name:'Postmark',selected:false}
    ];
}

You can then bind the checkbox value to the “selected” property of the view model.

<ul class="unstyled" data-bind="foreach: apps">
    <li>
        <label>
            <input type="checkbox" data-bind="checked: selected" />
        </label>
    </li>
</ul>

In order to get all the selected items, you again have several options.  Ultimately you’ll be iterating over your array of models and returning the ones that have the selected property set to “true”.  You can do this manually, with Knockout array utilities, or using a third party library like jQuery or Underscore.

var viewModel = function(){
    ...

    //using javascript
    self.selectedItems = function(){
        var selectedItems = [];
        for(var i = 0, len = self.apps.length; i < len; i++) {
            if(self.apps[i].selected){
                selectedItems.push(self.apps[i]);
            }
        }
        return selectedItems;
    }

    //using knockout utils
    self.selectedItemsKO = function(){
        return ko.utils.arrayFilter(self.apps, function(app){
            return app.selected;
        });
    };

    //using jquery
    self.selectedItemsJQ = function(){
        return $.grep(self.apps, function(app){
            return app.selected;
        });
    };

    //using underscore
    self.selectedItems_ = function(){
        return _.filter(self.apps, function(app){
            return app.selected;
        });
    };
}

Not bad, but we can do more.

Displaying Selected Items

So we have our list of selected items and we can retrieve the list from our selectedItems function.  This is all we need if we’re doing most of our data work behind the scenes using plain JavaScript.  We can easily grab all the selected items and send a data package to the server using AJAX.  If we want to display our list of selected items on the page separately from the list of checkboxes, then a little bit of refactoring is in order.

Any time we’re displaying changes in the state or data of our view models, we should be working through Knockout.  Right now our view model is mostly plain JavaScript.  We want to display a non-editable listing of all the selected items, which updates automatically when the selection changes; we want our selected list to be observable.  Since our UI and model decisions have us working both with checkboxes and returning secondary array of selected items, we can do this in a bit less code.

The first thing we want to do in our refactor is create an observableArray property on our view model which will hold the selected items.  We’ll just comment out our selectedItems function which we’re replacing to show the difference in code. Since we wont be using the selected property on the view models in our array anymore, we can get rid of it. We’ll also get rid of the name property too and just leave an array of strings. I’ll take a look at what happens if we don’t do that in a moment.

var viewModel = function(){
    var self = this;
    self.apps = [
        'Freckle',
        'Beanstalk',
        'DropBox',
        'Postmark'
    ];

    self.selectedItems = ko.observableArray([]);

    //using javascript
    /*self.selectedItems = function(){
        var selectedItems = [];
        for(var i = 0, len = self.apps.length; i < len; i++) {
            if(self.apps[i].selected){
                selectedItems.push(self.apps[i]);
            }
        }
        return selectedItems;
    }*/
}

Much simpler.

Now we can use a great feature of the checked binding to populate our new selectedItems observable array.  The checked binding can bind one checkbox to one boolean property, but it can also bind a checkbox to an array.  By switching the target of our checked binding to selectedItems, we now have Knockout automatically adding and removing the bound items or view models to our selectedItems array for us. It’s important to notice that the selectedItems array is in the parent context when we’re inside our foreach binding for the apps array. We’ll need to use the $parent variable to reach it.

There’s one more catch with this approach; the checkboxes’ html value attribute needs to be unique in order for this to work the way we want. We can address this by adding a value binding to our checkboxes and set it to the string we’re currently binding by using the Knockout $data variable.

<ul class="unstyled" data-bind="foreach: apps">
    <li>
        <label>
            <input type="checkbox" data-bind="value: $data, checked: $parent.selectedItems" />
        </label>
    </li>
</ul>

We can then bind our selectedItems array to our secondary view of the data, and we’re done!

<h3>The Results</h3>
<ul class="unstyled" data-bind="foreach: selectedItems">
    <li data-bind="text:$data"></li>
</ul>

Nice.

How to Screw This Up

As you saw in the last section, everything needs to be just right for this “low code” approach to work. We may need to use $parent and $data in our bindings. We have only strings in our source data array, and we get strings in our selectedItems array. What if we hadn’t simplified our data down to just strings? What if we were still working with objects like this?

var viewModel = function(){
    var self = this;
    self.apps = [
        {name:'Freckle'},
        {name:'Beanstalk'},
        {name:'DropBox'},
        {name:'Postmark'}
    ];
...
}

The first place we’d see the impact here is in the bindings. Instead of using $data, we’re back to binding to the name property. If you’re familiar with binding html select tags, you might expect that we can bind our entire object as the value of the checkbox and see that object show up in our selectedItems array. Unfortunately, as of Knockout 2.0.2 this isn’t supported. The value binding when used on a checkbox will only take a string, and so our selectedItems array will only contain strings even if our source data is an object.

<ul class="unstyled" data-bind="foreach: apps">
    <li>
        <label>
            <!-- bind the name property of our object as the checkbox value -->
            <input type="checkbox" data-bind="value: name, checked: $parent.selectedItems" />
        </label>
    </li>
</ul>

<ul class="unstyled" data-bind="foreach: selectedItems">
    <!-- our selectedItems array contains only strings, not full objects -->
    <li data-bind="text:$data"></li>
</ul>

If you wanted your selectedItems array to contain the object, you’re back to using a function to find the items from the original data array as before. You can match the name or an Id if that’s what your checkbox value was bound to.

Nice Trip. Where Are We?

So we went from a custom filter function, to quick binding using checked against an array, and then back to using functions when our source data became complex. Every change in our code was predicated by a different need in the UI, or a different structure in the view models. Ultimately, I’d love to see the checked binding in the Knockout library support full objects the way that the options binding does. We’ve got two more blog posts to go in this series. Maybe we’ll get there yet.