Knockout checked binding and select all children collection

旧时模样 提交于 2020-01-07 03:45:07

问题


I have a Knockout component which template prints two nested lists of items (checkboxes in my interface): a first foreach loop iterates through some "parents" items, a second (and nested) list iterates through each of the parents items and, if children are found, prints the children. The requirement is that if the user clicks on a parent item (that is, on a parent checkbox), all the children checkboxes become checked.

In my code, both parents and children checkboxes listen to an observable that is first set to false and then, when a parent is selected, is set to true - result: that parent's children get selected. My issue is that, this way, since every checkbox listen to the same observable, I end up with ALL the checkboxes selected, not only my selected parents + children, which is what I want.

Not sure how I should approach this issue some other way, here is my code:

ko.components.register("types",{ 
viewModel: function(){
var self = this;

// Data
self.types = ko.observableArray();
self.isPChecked = ko.observable(false);
nut.structuredDocumentTypes()
  .done(function (types) {
    self.types(types);
  });

// Behaviours
self.selectChildren = function(parent) {
  self.isPChecked(true);
  console.log(self.isPChecked(),parent);
}
},
template: 
'<div id="doctypes" class="wfp-form wfpTreeList">\
  <h2 class="hidden">Document Types</h2>\
  <ul class="wfpList" data-bind="foreach: types">\
    <!-- ko if: $index() > 0 -->\
    <li class="root">\
      <input\
        type="checkbox"\
        name="types"\
        class="wfp-checkbox"\
        data-select="multi"\
        data-bind="\
          checked: $component.isPChecked,\
          event: { change: $component.selectChildren },\
          value: $data.id">\
      <input\
        type="checkbox"\
        name="acc"\
        data-bind="\
          attr: { class: \'lvl\' + $data.id, id: \'acc\' + $data.id }">\
      <label\
        data-bind="attr: { for: \'acc\' + $data.id }">\
          <i class="ico-angle-down"></i>\
      </label>\
      <span data-bind="text: $data.label" class="root-title"></span>\
      <ul data-bind="attr: { \'data-lvl\': \'lvl\' + $index(), id: \'lvl\' + $data.id }">\
        <!-- ko foreach: $data.members -->\
          <li>\
            <label data-bind="attr: { for: \'dt-child-\' + $parent.id + \'-\' + $index() }">\
              <input\
                name="types"\
                type="checkbox"\
                data-bind="\
                  checked: $component.isPChecked,\
                  value: $data.id,\
                  attr: { id: \'dt-child-\' + $parent.id + \'-\' + $index() }">\
              <span data-bind="text: $data.label"></span>\
            </label>\
          </li>\
        <!-- /ko -->\
      </ul>\
    </li>\
  <!-- /ko -->\
  </ul>\
</div>'
});

UPDATE: JSON looks like this:

[{
"id": 4,
"members": [
    {
        "last_modified_date": "2016-08-04T14:59:25.958Z",
        "id": 31,
        "label": "Backgrounders & Information Notes"
    },
    {
        "last_modified_date": "2016-08-04T14:59:25.961Z",
        "id": 32,
        "label": "Biographies"
    },
],
"label": "Event-/Stakeholder related documentation",
},
{
"id": 2,
"members": [
    {
        "last_modified_date": "2016-08-04T14:59:25.875Z",
        "id": 1,
        "label": "Books"
    },
    {
        "last_modified_date": "2016-08-04T14:59:25.878Z",
        "id": 2,
        "label": "Briefs, Summaries, Synthesis, Highlights"
    },
],
"label": "Publications"
}]

Thanks all.


回答1:


You should slightly prepare your data by extending doctypes with computed property that depends on the state of respective members. Also members should contain observable property to track their checked state.

The best place to implement such preparations will be (I guess) the callback where you initialize types property with decoded JSON response:

nut.structuredDocumentTypes()
    .done(function (types) {
        // we'll prepare types here
        self.types(types);
    });

So using the approach from the fiddle mentioned by Roy J (and authored by user3297291) in comments you will get something like:

nut.structuredDocumentTypes()
    .done(function (types) {

        types.forEach(function(type){
            type.members.forEach(function(member){
                member.isSelected = ko.observable(false);
            });
            type.isSelected = ko.computed({
                read: function(){
                    return type.members.every(function(member){
                        return member.isSelected();
                    });
                },
                write: function(selected){
                    type.members.forEach(function(member){
                        member.isSelected(selected);
                    });
                }
            });
        });

        self.types(types); // insert prepared data
    });

The code is copied from my fiddle where I'd decomposed your component to the usual view model, and property names are different. Pay your attention to changes in data-bind attributes too!

https://jsfiddle.net/ostgals/z8vshLow/



来源:https://stackoverflow.com/questions/38809102/knockout-checked-binding-and-select-all-children-collection

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!