knockout.js中的绑定多下拉列表



我有一个多下拉列表,需要执行以下操作:
1.确保在一个下拉列表中选择值时,它不会出现在其他下拉列表中(在此处找不到合适的解决方案)
2.当选择值"Text"时,将显示一个文本字段(<input>),而不是"是/否"下拉列表
3."选择选项"将只出现在第一行(仍在处理)
4.确保如果选择了"文本",它将始终位于顶部(仍在处理)

JSFiddle

HTML

  <div class='liveExample'> 
   <table width='100%'>
                <tbody data-bind='foreach: lines'>
                    <tr>
                        <td>
                            Choose option:
                        </td>
                        <td>
                            <select data-bind='options: filters, optionsText: "name", value: filterValue'> </select>
                        </td>
                        <td data-bind="with: filterValue">
                            <select data-bind='options: filterValues, optionsText: "name", value: "name"'> </select>
                        </td>
                        <td>
                            <button href='#' data-bind='click: $parent.removeFilter'>Remove</button>
                        </td>
                    </tr>
                </tbody>
            </table>
            <button data-bind='click: addFilter'>Add Choice</button>

JAVASCRIPT:

var CartLine = function() {
    var self = this;
    self.filter = ko.observable();
    self.filterValue = ko.observable();
    // Whenever the filter changes, reset the value selection
    self.filter.subscribe(function() {
        self.filterValue(undefined);
    });
};
var Cart = function() {
    // Stores an array of filters
    var self = this;
    self.lines = ko.observableArray([new CartLine()]); // Put one line in by default
    // Operations
    self.addFilter = function() { self.lines.push(new CartLine()) };
    self.removeFilter = function(line) { self.lines.remove(line) };
};

ko.applyBindings(new Cart());

我将在此请求您的协助!主要针对第一个问题。

谢谢
Mike

如果您想根据UI中已经选择的选项来限制选项,您需要确保每个cartLine都有自己的过滤器阵列。让我们在构造函数中这样传递:

var CartLine = function(availableFilters) {
  var self = this;
  self.availableFilters = availableFilters;
  // Other code
  // ...
};

您必须使用这个新的视图模型属性,而不是全局filters数组:

<td>
  <select data-bind='options: availableFilters, 
    optionsText: "name", 
    value: filterValue'> </select>
</td>

现在,我们必须找出在创建新的cartLine实例时哪些过滤器仍然可用。Cart管理所有的lines,并具有addFilter功能。

self.addFilter = function() {
  var availableFilters = filters.filter(function(filter) {
    return !self.lines().some(function(cartLine) {
      var currentFilterValue = cartLine.filterValue();
      return currentFilterValue &&
        currentFilterValue.name === filter.name;
    });
  });
  self.lines.push(new CartLine(availableFilters))
};

新的CartLine实例只获取尚未在任何其他行中使用的筛选器。(注意:如果你想在旧的浏览器中使用Array.prototype.some,你可能需要一个polyfill)

剩下的只是一个用户体验决策,而不是"编码决策":你希望用户在添加新的"选择"后能够更改以前的"选项"吗?如果是这种情况,您将需要创建计算的availableFilters数组,而不是普通的数组。

这是一个分叉小提琴,其中包含我在上面发布的代码:http://jsfiddle.net/ztwcqL69/请注意,您可以创建加倍的选项,因为添加新选项后,选项仍然可以编辑。如果你评论想要的行为是什么,我可以帮你弄清楚如何做到。这可能需要一些更激烈的改变。。。我提供的解决方案更像是一个指向正确方向的指针。

编辑:我为没有提供最终解决方案而感到难过,所以这里有另一种方法:

如果你想回顾性地更新availableFilters,你可以这样做:

CartLine获得对其siblings(其他购物车行)的引用,并通过使用siblingsfilterValue:的ko.computed创建对任何更改的订阅

var CartLine = function(siblings) {
  var self = this;
  self.availableFilters = ko.computed(function() {
    return filters.filter(function(filter) {
      return !siblings()
        .filter(function(cartLine) { return cartLine !== self })
        .some(function(cartLine) {
        var currentFilterValue = cartLine.filterValue();
        return currentFilterValue &&
          currentFilterValue.name === filter.name;
      });
    });
  });
  // Other code...
};

创建新的购物车行,如下所示:self.lines.push(new CartLine(self.lines))。使用空数组初始化,然后使用addFilter推送第一个CartLine

关于第2点:您可以创建一个基于filterValue进行排序的计算可观察值:

self.sortedLines = ko.computed(function() {
  return self.lines().sort(function(lineA, lineB) {
    if (lineA.filterValue() && lineA.filterValue().name === "Text") return -1;
    if (lineB.filterValue() && lineB.filterValue().name === "Text") return 1;
    return 0;
  });
});

点3:将其移动到foreach之外。

第4点:使用if绑定:

<td data-bind="with: filterValue">
  <!-- ko if: name === "Text" -->
  <input type="text">
  <!-- /ko -->
  <!-- ko ifnot: name === "Text" -->
  <select data-bind='options: filterValues, optionsText: "name", value: "name"'> </select>          
   <!-- /ko -->
<td>

更新了包含以下代码的fiddle:http://jsfiddle.net/z22m1798/

最新更新