dimiguel
dimiguel

Reputation: 1579

Knockout checkedValue binding not working when array is made up of objects

I am having a weird issue with a page on my site. I am using ASP.NET MVC to send model data to the page.

I am showing a list of checkboxes based on objects in a Javascript array. When the page loads, it shows the items properly but does not select the ones in the selected items array.

Here's what the HTML looks like.

<!-- ko foreach: items -->
    <p>
        <input type="checkbox"
               data-bind="checkedValue: $data, checked: $root.selectedItems" />
        <span data-bind="text: Name"></span>
    </p>
<!-- /ko -->

Here's what the Javascript looks like.

var Item = function (id, name) {
    this.Id = id;
    this.Name = name;
};

var model = {
    items = ko.observableArray([]),
    selectedItems = ko.observableArray([])
};

@foreach (var serverItem in serverItems) {
    @:model.items.push(new Item('@serverItem.Id', '@serverItem.Name'));
}
@foreach (var selectedServerItem in selectedServerItems) {
    @:model.selectedItems.push(new Item('@selectedServerItem.Id', '@selectedServerItem.Name'));
}

ko.applyBindings(model);

You would think that it would get selected because the objects are identical, but it doesn't. And when I do select the checkboxes, it adds additional items to the selectedItems array instead of using the existing ones.

['1', 'Business To Business'], ['2', 'Business To Consumer'], ['1', 'Business To Business']

Can anyone explain to me why this is happening? Why is Knockout not understanding that checkboxes need to be checked based on objects and not primitives?

Solution I ended up with:

var items = [];
@foreach (var serverItem in serverItems) {
    @:items[@serverItem.Id] = {Id: '@serverItem.Id', Name: '@serverItem.Name'};
    @:model.items.push(items[@serverItem.Id]);
    if (selectedServerItems.Any(si => si.Id == serverItems.Id)
        @:model.selectedItems.push(items[@serverItem.Id]);
}

Now the checkboxes are properly selected when the page loads.

Upvotes: 1

Views: 1474

Answers (1)

Anthony Chu
Anthony Chu

Reputation: 37540

The objects are identical, but they are different instances. Knockout does an == (or maybe ===) to compare the items, and in JavaScript that will always return false for 2 different instances of an object, even if internally they are the same. This works fine for strings, however.

var foo1 = {foo: 1};
var foo2 = {foo: 1};
var foo3 = foo1;

console.log(foo1 == foo2); // false
console.log(foo1 == foo3); // true

You can see that in action in this fiddle... http://jsfiddle.net/Nk86C/1/

Upvotes: 3

Related Questions