问题
So here it is... I am attempting to build a data-grid with Knockout.js. I want to build it from scratch (skill building exercise), so I don't want to use KoGrid or SimpleGrid.
The issue I am having is that I want to be able to filter the results based on a text input. This filter has to go through each object and filter on ONLY the keys that match a column's value property.
Example
Filtering with the value '1' will return both of data's objects with properties that contain 1. (ID 1 and ID 3).
JSFIDDLE
HTML
<div data-bind="foreach: filteredItems">
<p data-bind="text: LastName"></p>
</div>
<p>Filter:
<input data-bind="value: filter, valueUpdate: 'keyup'" />
</p>
<p>Filter Value: <span data-bind="text: filter"></span></p>
JavaScript
var data = [{
Id: 1,
LastName: "Franklin"
}, {
Id: 2,
LastName: "Green"
}, {
Id: 3,
LastName: "1"
}];
var columns = [{
value: 'Id'
}, {
value: 'LastName'
}];
var Filtering = function (data, columns) {
var self = this;
self.items = ko.observableArray(data);
self.columns = ko.observableArray(columns);
self.filter = ko.observable();
self.filteredItems = ko.computed(function () {
var filter = self.filter();
console.log(filter);
if (!filter) {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function (item) {
console.log('Filtering on Item');
ko.utils.arrayForEach(self.columns(), function (c) {
var val = item[c.value];
if (typeof val === 'number') {
val = val.toString();
}
console.log('Filtering on Column');
return val.toLowerCase().indexOf(filter.toLowerCase()) > -1;
});
});
}
});
};
ko.applyBindings(new Filtering(data, columns));
It works great statically setting the c.value
in item[c.value]
, but when I try to loop through the array of self.columns()
I do not get results returned.
Recs
I have jQuery, Knockout.js 3.0, and underscore.js at my disposal.
Any help is greatly appreciated.
回答1:
Just few problems in your code :
- a Boolean needs to be returned as output of ko.utils.arrayFilter
- you need a sort of sum up for arrayForEach as your filter is an OR
I changed your code to address those details :
var Filtering = function (data, columns) {
var self = this;
self.items = ko.observableArray(data);
self.columns = ko.observableArray(columns);
self.filter = ko.observable();
self.filteredItems = ko.computed(function () {
var filter = self.filter();
console.log(filter);
if (!filter) {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function (item) {
console.log('Filtering on Item');
var matching = -1;
ko.utils.arrayForEach(self.columns(), function (c) {
var val = item[c.value];
if (typeof val === 'number') {
val = val.toString();
}
console.log('Filtering on Column');
matching+= val.toLowerCase().indexOf(filter.toLowerCase())+1;
});
console.log(matching);
return matching>=0;
});
}
});
};
Worked for me there : http://jsfiddle.net/Lzud7fjr/1/
回答2:
Your filtering case isn't returning anything. ko.utils.arrayFilter()
should return a truthy value if the item should be included in the result. However since it doesn't return anything, nothing is included in the result. You'll need to rewrite it so it will return something.
I suppose you could change the inner ko.utils.arrayForEach()
call to a filter and return true if the result is non-empty.
self.filteredItems = ko.computed(function () {
var filter = self.filter();
console.log(filter);
if (!filter) {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function (item) {
console.log('Filtering on Item');
var result = ko.utils.arrayFilter(self.columns(), function (c) {
var val = item[c.value];
if (typeof val === 'number') {
val = val.toString();
}
console.log('Filtering on Column');
return val.toLowerCase().indexOf(filter.toLowerCase()) > -1;
});
return !!result.length;
});
}
});
来源:https://stackoverflow.com/questions/26374056/knockout-filtering-objects-in-observable-arrays-by-multiple-object-properties