Html Code
<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>
Sample data (simplified):
var inventory = [{
Id: "1",
SerialNo: "00001"
},
{
Id: "2",
SerialNo: "00002"
},
{
Id: "3",
SerialNo: "10003"
},
{
Id: "4",
SerialNo: "10004"
}
];
Knockout Code:
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();
}
});
}
Everything works fine in terms of filtering the list based on the serial number the user inputs. Example here https://jsfiddle.net/JohnnyCodes/5h9pnqLg/.
Use Case:
Problem is, it's like there's some kind of recursive reference and the list starts to get wonky.
Enter 1 to filter then change it to 0 then change it back to 1
Here's an example, https://jsfiddle.net/JohnnyCodes/cs4z9xpg/5/
Pretty some this is something really simple and stupid I've overlooked but I've been going around in circles for a while now. Wouldn't mind another pair of eyes.
Thanks
The problem is with the following line
// Default the list to include the selected items.
filteredList(_root.selectedEquipment());
Observables use a standard array as their underlying data, but when you initialize an observable using an existing array the observable uses that array as the underlying array instead of creating a new array, and all references to it are preserved. This means that on the next loop through even though your filteredList
var has a new scope, it then gets pointed back to the same existing array (selectedEquipment) and that array still has values in it from the last recalc.
Changing the line so that items are added to the observable created at the top of the function without re-using the root level selectedEquipment array should solve the problem:
ko.utils.arrayPushAll(filteredList, _root.selectedEquipment());