Reputation: 10478
Using the example provided here: http://knockoutjs.com/documentation/checked-binding.html combined with the answer here: Binding a list of objects to a list of checkboxes
I am trying to bind an observable array to a checkbox list. Everything works when you start fresh. Clicking a checkbox will add the item to the selected items array, ahd submitting the page w/ajax it comes over fine. But if you try to load existing data, it doesn't work.
Please see this example: https://jsfiddle.net/fwg3efv6/
If you set the binding on the checkbox input with checkedValue: $data
as suggest in the KO example. And then try and supply selectedPeople on page load here:
selectedPeople: ko.observableArray([new Person(1, "Fred", 25)])
But if you set binding to checkingValue: id
and populate model like so:
selectedPeople: ko.observableArray([1])
It starts checked no problem.
When binding a whole object, if you check that checkbox again, then selected people will now have 2 objects in it both with the same properties. If it works when starting fresh via unchecking and checking, why is trying to start by binding some values already checked not working?
function Person(id,name,age) {
this.id = id;
this.name = name;
this.age = age;
}
var listOfPeople = [
new Person(1, 'Fred', 25),
new Person(2, 'Joe', 60),
new Person(3, 'Sally', 43)
];
var viewModel = {
people: ko.observableArray(listOfPeople),
selectedPeople: ko.observableArray([new Person(1, "Fred", 25)])
};
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: people">
<li>
<input type="checkbox" value="" data-bind="checkedValue: $data, checked: $parent.selectedPeople"><span data-bind="text: name"></span>
</li>
</ul>
I'm using .NET MVC to supply my data. So my initial model property looks like this. toJS is custom MVC method that spits out a valid JS object as string. I confirm by telling in model.selectedPeople in console and it's corectly populated:
selectedPeople: ko.observableArray(@Html.ToJs(Model.SelectedPeople)),
Eric Phillips Answers let me to the correct answer which is seen below::
//outside ko model declaration:
var listOfPeople = @Html.ToJson(Model.People);
//inside ko model declaration:
selectedPeople: ko.observableArray(getSelectedItems(@Html.ToJson(Model.SelectedPeople), listOfPeople, "id"))
function getSelectedItems(selectedItems, availableItems, propertyComparer) {
var selectedPropertyComparers = selectedItems.map(function(a) { return a[propertyComparer]; });
return availableItems.filter(function(item) {
return selectedPropertyComparers.indexOf(item[propertyComparer]) !== -1;
});
}
Upvotes: 0
Views: 336
Reputation: 54638
The reason is because:
var listOfPeople = [
new Person(1, 'Fred', 25),
new Person(2, 'Joe', 60),
new Person(3, 'Sally', 43)
];
console.log(new Person(1, "Fred", 25) == listOfPeople[1])
returns
false
they are not referencely the same object.
instead:
var fred = new Person(1, 'Fred', 25)
var listOfPeople = [
fred,
new Person(2, 'Joe', 60),
new Person(3, 'Sally', 43)
];
console.log(fred == listOfPeople[1])
returns
true
so then you could
selectedPeople: ko.observableArray([fred])
or
// preselect 2 people
selectedPeople: ko.observalbeArray([listOfPeople[1], listOfPeople[2]])
Upvotes: 2