在knockout.js中绑定多个下拉列表

时间:2016-05-09 11:22:42

标签: javascript html knockout.js

我有一个多下拉列表,我需要执行以下操作:
1.确保在一个下拉列表中选择值时,它不会出现在其他列表中(此处无法找到正确的解决方案)。
2.选择值&#34; Text&#34;文本字段(<input>)将显示而不是“是/否”下拉列表 3.&#34;选择选项&#34;将仅出现在第一行(仍在使用它) 4.确保如果&#34; Text&#34;如果被选中,它将始终位于顶部(仍在处理它)。

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());

我会在这里呼吁你的帮助!主要是第一个问题。

谢谢!
麦克

1 个答案:

答案 0 :(得分:2)

如果您想根据UI中已选择的选项限制选项,您需要确保每个cartLine都有自己的过滤器数组。让我们在构造函数中传递它,如下所示:

var CartLine = function(availableFilters) {
  var self = this;
  self.availableFilters = availableFilters;

  // Other code
  // ...
};

您必须使用此新的viewmodel属性而不是全局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

唯一剩下的就是用户体验决策而不是编码决策&#34;:您是否希望用户能够改变以前的选择&#34;添加新的?如果是这种情况,您需要创建计算availableFilters数组而不是普通数组。

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

编辑:我觉得不提供最终解决方案很糟糕,所以这是另一种方法:

如果您想要追溯更新availableFilters,可以这样做:

CartLine获取对其siblings(其他购物车行)的引用,并通过使用ko.computed siblings创建对所有更改的订阅他们的filterValue

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))。使用空数组启动,然后使用CartLine推送第一个addFilter

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

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>

包含此代码的更新小提琴:http://jsfiddle.net/z22m1798/