温馨提示:本文翻译自stackoverflow.com,查看原文请点击:knockout.js - Filter selectlist options based on user input while retaining items that were already selected
knockout.js

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

发布于 2020-04-29 10:02:44

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/

用例:

  • 用户在(ID:1,Serial 00001)中选择了第一项
  • 然后,用户在序列号过滤器中输入1
    • 我希望列表包含所选项目(编号:1,序列号00001)以及序列号以1开头的两个项目

问题是,就像有某种递归引用一样,列表开始变得晦涩难懂。

输入1进行过滤,然后将其更改为0,然后将其更改回1

这是一个示例,https://jsfiddle.net/JohnnyCodes/cs4z9xpg/5/

相当有些东西,这是我忽略的真正简单而愚蠢的东西,但我已经兜圈子了一段时间。不会介意另一双眼睛。

谢谢

查看更多

提问者
Tim Leclair
被浏览
19
Jason Spake 2020-02-11 06:40

问题是与以下行

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

可观察对象使用标准数组作为其基础数据,但是当您使用现有数组初始化可观察对象时,可观察对象将使用该数组作为基础数组而不是创建新数组,并且保留对其的所有引用。这意味着即使您的filteredListvar具有新的作用域,在下一个循环中,它也会被指向相同的现有数组(selectedEquipment),并且该数组在上一次重新计算后仍具有值。

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

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