Reputation: 3175
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).
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.
Upvotes: 0
Views: 4088
Reputation: 2518
Just few problems in your code :
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/
Upvotes: 1
Reputation: 134521
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;
});
}
});
Upvotes: 1