根据用户输入过滤选择列表选项,同时保留已选择的项目

Filter selectlist options based on user input while retaining items that were already selected

Html代码

<input type="text" class="radius" placeholder="SerialNo" data-bind="textInput: fromSerialNo" />
<br/>
<select data-bind="options: filteredInventoryList,
                   optionsText: function(item) {
                     return item.Id + ' (' + item.SerialNo + ')';
                   },
                   selectedOptions: selectedEquipment                   
                   " size="5" multiple="multiple" style="width: 300px;"></select>

样本数据(简体):

  var inventory = [{
      Id: "1",
      SerialNo: "00001"
    },
    {
      Id: "2",
      SerialNo: "00002"
    },
    {
      Id: "3",
      SerialNo: "10003"
    },
    {
      Id: "4",
      SerialNo: "10004"
    }
  ];

淘汰代码:

function viewModel() {
  var _root = this;
  // User input serialNo for filtering
  _root.fromSerialNo = ko.observable();
  // selectedOptions of the select list
  _root.selectedEquipment = ko.observableArray();
  // parent list of all equipment
  _root.fromInventoryList = ko.observableArray(inventory);

  // filtered list based on serialNo user input (should including the previously selected items)
  _root.filteredInventoryList = ko.computed(function() {
      var filteredList = ko.observableArray(null);

      if (!_root.fromSerialNo()) {
        // This works perfect, allows the user to select one or more item from the list.
        return _root.fromInventoryList();
      }
      else {
        // The following works and allow users to filter the parent list of equipment

        // Only show items that begin with the SerialNo entered
        filteredList(ko.utils.arrayFilter(_root.fromInventoryList(), function (item) {
                    return item.SerialNo.startsWith(_root.fromSerialNo());
                }));

        return filteredList();
      }
  });
}

根据用户输入的序列号过滤列表,一切正常。这里的示例 https://jsfiddle.net/JohnnyCodes/5h9pnqLg/.

用例:

问题是,好像存在某种递归引用,列表开始变得不稳定。

输入1过滤然后改成0再改回1

举个例子,https://jsfiddle.net/JohnnyCodes/cs4z9xpg/5/

相当一部分这是我忽略的非常简单和愚蠢的事情,但我已经绕圈子转了一段时间了。不介意多一双眼睛。

谢谢

问题出在以下行

// Default the list to include the selected items.
filteredList(_root.selectedEquipment());

Observable 使用标准数组作为它们的底层数据,但是当您使用现有数组初始化 observable 时,observable 使用该数组作为底层数组而不是创建新数组,并且对它的所有引用都会被保留。这意味着在下一个循环中,即使您的 filteredList var 有一个新的范围,它也会被指向相同的现有数组 (selectedEquipment),并且该数组中仍然包含上次重新计算的值。

更改行以便将项目添加到在函数顶部创建的可观察对象而不重新使用根级 selectedEquipment 数组应该可以解决问题:

ko.utils.arrayPushAll(filteredList, _root.selectedEquipment());

Jason Spake 提供的建议很有帮助,但只解决了部分问题。

这是我使用的解决方案,希望这有时能对某人有所帮助:

_root.filteredInventoryList = ko.computed(function () {
            var filteredResults = ko.observableArray([]);

            // Filter fromInventoryList
            if (!_root.fromSerialNo()) {
                return  ko.utils.arrayPushAll(filteredResults(), _root.fromInventoryList());
            }
            else {
                // Add the selected items ot the array
                ko.utils.arrayPushAll(filteredResults(), _root.selectedEquipment());

                // Only show items that match
                filteredResults().push.apply(filteredResults(), ko.utils.arrayFilter(_root.fromInventoryList(), function (item) {
                    // Important, don't add the item if it was added with selectedEquipment
                    if (filteredResults().indexOf(item) < 0) {
                        return item.SerialNo().startsWith(_root.fromSerialNo());
                    }
                }));

                return filteredResults();
            }
        });