使用foreach的同一过滤列表的级联列表框

时间:2016-05-11 20:05:24

标签: javascript knockout.js

我需要创建一个级联列表框,如下图中的图像,但每个列表框使用相同的数据列表,但使用不同的过滤器。 enter image description here 我有this code我可以用两个选项来完成它,但正如你所看到的,我使用两个observable来读取每个选择的de值。有没有办法使用foreach绑定创建这个,如果我可能需要10个选择,例如?



var ViewModel = function() {
  self = this;
  self.family = ko.mapping.fromJS(data.family);
  self.selected = ko.observable();
  self.selected2 = ko.observable();


  self.filteredFamily = ko.computed(function() {
    var list = ko.utils.arrayFilter(self.family(), function(item) {

      if (self.selected() == undefined) return;
      return item.parent() === self.selected().name();
    });

    return list;
  });
}

var data = {
  family: [{
    name: "John",
    parent: null
  }, {
    name: "Mike",
    parent: null
  }, {
    name: "Alice",
    parent: "John"
  }, {
    name: "Paul",
    parent: "John"
  }]
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<select data-bind="options: family, optionValue: name,optionsText: 'name',value:selected, optionsCaption: 'Choose...'"></select>
<select data-bind="options: filteredFamily, optionValue: name,optionsText: 'name',value:selected2, optionsCaption: 'Choose...'"></select>
&#13;
&#13;
&#13;

[增订]

抱歉,我认为我的问题没有很好地表达,我需要在列表框中选择一些内容时,下一个用最后一个lisbox的数据更新他的数据。

2 个答案:

答案 0 :(得分:0)

不确定。但是foreach需要一个(可观察的)数组,我建议将“小部件”代码封装在一个单独的视图模型中。这是一个天真的设置:

var SelectionViewModel = function(baseOptionsObsArray, filter) {
  var self = this;
  
  self.filteredOptions = ko.computed(function() {
    var val = ko.utils.unwrapObservable(filter.val); // Allow observable and plain property
    return baseOptionsObsArray().filter(function(opt) {
      return ko.utils.unwrapObservable(opt[filter.prop]) === val;
    });
  });
  
  self.filter = filter;
  
  self.selected = ko.observable();
};

var ViewModel = function(data) {
  var self = this;
  self.family = ko.mapping.fromJS(data.family);
  self.selectables = ko.observableArray([
    new SelectionViewModel(self.family, { name: "No Parent", prop: "parent", val: null }),
    new SelectionViewModel(self.family, { name: "Parent is John", prop: "parent", val: "John" }),
    new SelectionViewModel(self.family, { name: "Has a pet", prop: "hasPet", val: true }),
    new SelectionViewModel(self.family, { name: "Name is Alice", prop: "name", val: "Alice" }),
  ]);
};

var data = {
  family: [{ name: "John", parent: null, hasPet: true }, 
           { name: "Mike", parent: null, hasPet: true }, 
           { name: "Alice", parent: "John", hasPet: false },
           { name: "Paul", parent: "John", hasPet: true }
  ]
};

ko.applyBindings(new ViewModel(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<!-- ko foreach: selectables -->
<span data-bind="text: filter.name"></span>
<select data-bind="options: filteredOptions,
                   optionValue: name,
                   optionsText: 'name', 
                   value: selected,
                   optionsCaption: 'Choose...'"></select>
<hr>
<!-- /ko -->

您可以轻松添加诸如使用通配符,否定等更高级过滤之类的内容。这仍然是读者的练习。

在任何情况下,我认为您的问题(如何确保您可以轻松创建10个选项和过滤的选项列表)将得到解答。

另一方面,如果你正在寻找更多钻取样式的选择框,我仍然建议使用单独的视图模型并嵌套。 E.g:

var SelectionViewModel = function(parent, everyone) {
  var self = this;
  
  self.selection = ko.observable();
  
  self.filteredOptions = everyone().filter(function(p) {
    console.log(p);
    if (!parent) { return !p.parent(); }
    return p.parent() === parent.name();
  });
  
  self.subSelection = ko.computed(function() {
    if (!self.selection()) { return null; }
    return new SelectionViewModel(self.selection(), everyone);
  });
};

var ViewModel = function(data) {
  var self = this;
  self.everyone = ko.mapping.fromJS(data.family);  
  self.familyHead = ko.observable(new SelectionViewModel(null, self.everyone));
};

var data = {
  family: [{ name: "John", parent: null }, 
           { name: "Mike", parent: null }, 
           { name: "Alice", parent: "John" },
           { name: "Graaw", parent: "John" },
           { name: "Paul", parent: "John" }, 
           { name: "Marc", parent: "Alice" },
           { name: "Fika", parent: "Alice" },
           { name: "Eric", parent: "Marc" }
  ]
};

ko.applyBindings(new ViewModel(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<script type="text/html" id="selectable">
<select data-bind="options: filteredOptions,
                   optionValue: name,
                   optionsText: 'name', 
                   value: selection,
                   optionsCaption: 'Choose...'"
        size="5"></select>
<!-- ko with: subSelection -->
<!-- ko template: 'selectable' --><!-- /ko -->
<!-- /ko -->
</script>

<!-- ko template: { data: familyHead, name: 'selectable' } --><!-- /ko -->

答案 1 :(得分:0)

我发现了如何做到这一点。这就是我正在做的事情:

var ViewModel = function() {
  self = this;
  self.family = ko.mapping.fromJS(data.family);
  self.selected = ko.observable();


  self.filteredFamily = ko.computed(function() {
    var list = ko.utils.arrayFilter(self.family(), function(item) {
      debugger;
      if (self.selected() == undefined) return;
      return item.parent() === self.selected().name();
    });
    debugger;
    return list;
  });
}

var data = {
  family: [{
    name: "John",
    parent: null
  }, {
    name: "Mike",
    parent: null
  }, {
    name: "Alice",
    parent: "John"
  }, {
    name: "Paul",
    parent: "John"
  }]
};
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>

<select data-bind="options: family, optionValue: name,optionsText: 'name',value:selected, optionsCaption: 'Choose...'"></select>
<select data-bind="options: filteredFamily, optionValue: name,optionsText: 'name',value:selected()[1], optionsCaption: 'Choose...'"></select>