Código 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>

Datos de muestra (simplificados):

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

Código de eliminación:

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

Todo funciona bien en términos de filtrado de la lista en función del número de serie que ingresa el usuario. Ejemplo aquí https://jsfiddle.net/JohnnyCodes/5h9pnqLg/.

Caso de uso:

  • El usuario seleccionó el primer elemento en (Id: 1, Serie 00001)
  • La usuario luego ingresa 1 en el filtro SerialNumber
    • Me gustaría que la lista contenga el elemento seleccionado (Id: 1, Serie 00001), así como los dos elementos cuyos números de serie comienzan con 1

El problema es que es como si hubiera algún tipo de referencia recursiva y la lista comienza a ponerse inestable.

Ingrese 1 para filtrar luego cámbielo a 0 luego cámbielo nuevamente a 1

Aquí hay un ejemplo, https://jsfiddle.net/JohnnyCodes/cs4z9xpg/5/

Bastante poco, esto es algo realmente simple y estúpido que he pasado por alto, pero he estado dando vueltas en círculos durante un tiempo. No le importaría otro par de ojos.

Gracias

0
Tim Leclair 10 feb. 2020 a las 22:17

2 respuestas

La mejor respuesta

El problema es con la siguiente línea.

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

Los observables usan una matriz estándar como sus datos subyacentes, pero cuando inicializa un observable usando una matriz existente, el observable usa esa matriz como la matriz subyacente en lugar de crear una nueva matriz, y todas las referencias a ella se conservan. Esto significa que en el siguiente ciclo a pesar de que su filteredList var tiene un nuevo alcance, luego vuelve a apuntar a la misma matriz existente (selectedEquipment) y esa matriz todavía tiene valores desde la última recalc.

Cambiar la línea para que los elementos se agreguen al observable creado en la parte superior de la función sin reutilizar el nivel raíz seleccionado La matriz de equipos debería resolver el problema:

ko.utils.arrayPushAll(filteredList, _root.selectedEquipment());
0
Jason Spake 10 feb. 2020 a las 22:40

La sugerencia que Jason Spake brindó fue útil, pero solo resolvió parte del problema.

Aquí está la solución que usé, espero que esto ayude a alguien en algún momento:

_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();
            }
        });
0
Tim Leclair 11 feb. 2020 a las 02:05