Search code examples
jqueryknockout.jsfaceted-search

Knockout Select All checkbox doesn't work after observable is deselected


I've created a faceted search page, which allows a user to select a checkbox for each facet they want to filter by. I've got it working to a degree except for one problem. A user can click a Select All button which will select all children or deselect all children.

The problem I'm running into is when a user deselects one of the options. From that point on, when clicking the 'All' checkbox, it will ignore that option. I'm new to Knockout, so this is throwing me for a loop. I'm hoping someone could shed some light on why this is happening.

Here is the working/not working facet checkbox list: http://jsfiddle.net/thadeus13/ep8rp9gp/1/

My ViewModel with the selectAll property is:

function ViewModel() {
    var self = this;

    var realModel = [];

    ko.utils.arrayForEach(results.ResourceFacets, function (item) {
        realModel.push({
            name: item,
            IsChecked: ko.observable(true)
        });
    });

    self.facets = realModel;

    self.selectAll = ko.computed({
        read: function () {
            var firstUnchecked = ko.utils.arrayFirst(self.facets, function (item) {
                return item.IsChecked() == false;
            });
            return firstUnchecked == null;
        },
        write: function (value) {
            ko.utils.arrayForEach(self.facets, function (item) {
                item.IsChecked(value);
            });
        }
    });
};

Solution

  • You're so close! Just a tiny typo in your binding statement with the closing } for the attr binding. It should be:

    <input type="checkbox" data-bind="attr: { id: name.replace(' ', '-').toLowerCase()}, checked: IsChecked " />
    

    Because of this typo then what's happening is that the checkboxes are being rendered by the template binding, but each one individually isn't actually being bound to anything at all. So the IsSelected binding isn't working as you expect, and it is just the browser itself that toggles things on and off as you click each item.

    I find it can help track down syntax bugs like this if you use carriage-returns in your HTML. This fragment would look good like:

    <input type="checkbox" data-bind="
        attr: {
            id: name.replace(' ', '-').toLowerCase()
        }, 
        checked: IsChecked
    " />
    

    which just helps you keep track of where your braces get to, especially in long bindings.

    Updated fiddle with this correction: http://jsfiddle.net/ep8rp9gp/3/