4

I know this topic has been addressed a few times but I'm having a bit of trouble here. I have the following in my view:

    <!-- ko foreach: documents -->
    <div>
        <input type="checkbox" data-bind="checked: $parent.checkItem(documentId)" />
    </div>
    <!-- /ko -->

In my viewModel:

var checkItem = function (checkedItem) {
    debugger;
    window.selectedDocuments.push(checkedItem);
};

I'm using window because another resource needs access to this array.

Right now, when I load the page the checkItem is hit one time for each document, which I don't think it should. I'm trying to monitor which documents have been selected, keeping an array updated (in this case, selectedDocuments).

Here's a fiddle with my attempt:

http://jsfiddle.net/PTSkR/36/

How can I make this work?

SB2055
  • 12,272
  • 32
  • 97
  • 202

2 Answers2

6

I think all you should have to do is use a checked data-binding with the observable array. Knockout will update the observable array automatically when you check an item.

Also note that I added a value binding that binds the value of each checkbox to the documentId.

View:

<!-- ko foreach: documents -->
    <div>
        <input type="checkbox" data-bind="checked: $parent.selectedDocuments, value: documentId" />
    </div>
<!-- /ko -->

<!-- ko foreach: selectedDocuments -->
        <div>
            <span data-bind="text: $data"></span>
        </div>
<!-- /ko -->

ViewModel:

var selectedDocuments = ko.observableArray();

var viewModel = {
    documents: [{"documentId": "1"}, {"documentId": "2"}, {"documentId": "3"}],
    selectedDocuments: selectedDocuments
};
ko.applyBindings(viewModel);

Example: http://jsfiddle.net/PTSkR/37/

As a side note, I would avoid attaching properties to window if at all possible. You could use a lightweight namespacing pattern or use a simple pub/sub system with KnockoutJS.

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • Wow, awesome. Thanks Andrew. "I would avoid attaching properties to window if at all possible." - why is that? – SB2055 Apr 30 '13 at 03:34
  • Thanks again man. I just tried to implement pub/sub and I'm super close to getting it working, question posted here if you have time: http://stackoverflow.com/questions/16307483/observable-arrays-with-knockout-publishing-subscribing – SB2055 Apr 30 '13 at 19:25
0

You will want to bind your checked binding either against an observable or an observableArray. When bound against an array, the checked binding with add and remove the value of the input from the array.

So, in your case, you could bind the input's value against the documentId against an array, which would result in an array of ids. Then, you can use a computed to create an array of the document's represented by those ids.

var viewModel = {
    documents: [{"documentId": "2"}, {"documentId": "3"}, {"documentId": "1"}],
    selectedDocumentIds: ko.observableArray()
};

//quick index to make lookup require less looping
viewModel.documentIndex = {};
ko.utils.arrayForEach(viewModel.documents, function(doc) {
   viewModel.documentIndex[doc.documentId] = doc; 
});

//computed to represent an array of the actual objects
viewModel.selectedDocuments = ko.computed(function() {
    return ko.utils.arrayMap(viewModel.selectedDocumentIds(), function(id) {
        return viewModel.documentIndex[id];
    });
});

Sample here: http://jsfiddle.net/rniemeyer/DCZKz/

RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211