Knockout - Filtering Objects in Observable Arrays

2019-09-01 10:17发布

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.

2条回答
老娘就宠你
2楼-- · 2019-09-01 10:49

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;
        });
    }
});
查看更多
迷人小祖宗
3楼-- · 2019-09-01 11:06

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/

查看更多
登录 后发表回答