0

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;
    });
}
SventoryMang
  • 10,275
  • 15
  • 70
  • 113
  • [You need to provide all your code](https://stackoverflow.com/help/mcve) **in the question itself**. This is always a requirement because if your fiddle disappears the question becomes useless for future readers. – Erik Philips Jan 24 '18 at 23:26

1 Answers1

2

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]])
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • Hmm I thought quality comparison is compared against the object as a string? So how would I do this with multiple people? I'd have to populate selectedPeople with the exact same objects I'm using to populate the list of available people yes? – SventoryMang Jan 24 '18 at 23:32
  • Definitely not. [SO Reference](https://stackoverflow.com/questions/1068834/object-comparison-in-javascript). – Erik Philips Jan 24 '18 at 23:32
  • Let me update, because you're using an MVVM framework but not all of it's capabilities. – Erik Philips Jan 24 '18 at 23:34
  • How do you determine if someone is preselected. You're example is so constrained I can't really answer your question. – Erik Philips Jan 24 '18 at 23:36
  • It's supplied via my server side code (using MVC). I'll update my question with what I tried, I feel I'm close just the code is wrong. – SventoryMang Jan 24 '18 at 23:49
  • I've updated my code. I was trying to use the "duplicate" array of selectedItems and use that to return the actual items in the original array, but right now for some reason it's returning all items in availableItems anyway, and none are still checked =/ – SventoryMang Jan 24 '18 at 23:58
  • I realized it's won't work because I'm still creating a separate array as the second parameter. So I don't know how to declare a model property and it's initial value? It seems like I need to supply model.People as the second parameter in the method? – SventoryMang Jan 25 '18 at 00:01
  • Well the question has definitely changed from the original. If you are trying to get objects out of an array by match, that is a completely different question. – Erik Philips Jan 25 '18 at 00:06
  • @SventoryMang I think you should focus on what you really want to do. As your question is `Binding to a checkbox list won't work when page loads`, I assume that you want to make your checkboxes checked in accordance with an array. You could follow Erik Phillips' suggestion. I also see that you have your own solution as you said that `if you set binding to checkingValue: id and populate model like so: selectedPeople: ko.observableArray([1]), It starts checked no problem.`. I don't know why you still made a SO question even you have had the answer youself. I prefer YOUR SOLUTION. – trgiangvp3 Jan 25 '18 at 01:28
  • Because that isn't how my pages work, we have a standard where everything we return to server page for selected items off lists and dropdowns should be an object (containing id and a string name). And while I could change it, that's horrible practice to change your app design because of technical issues. – SventoryMang Jan 25 '18 at 17:02
  • @ErikPhilips I was able to solve it based on the info you provided, if you want to edit your response to include the code I used, that might be helpful for future viewers. – SventoryMang Jan 25 '18 at 17:50