Knockout Performance - Filtering an Observable Arr

2019-07-31 17:54发布

I'm new to Knockout, and I'm trying to use it on a page showing a system's users and the roles that each user has.

The data is in an observableArray of users. The user object has a roles property, which is another observableArray. This second array contains an object for each role, with an ID and a boolean "granted" property.

I want to be able to display all of the users with a specific role, so there's a checkbox for each role - when one of these is checked, the list should show the users with that role.

The problem I've got is that filtering the 1,000 or so users by roles takes several seconds. Filtering by the text in the name is very quick (a few milliseconds), but filtering by role is not. I've put some timing code in, and the issue is the method I'm using to check whether the user has the selected role(s) so I'm just wondering whether there's a better way of doing it, maybe using some Knockout magic.

Below is the ko.computed on the view model that I'm using to do the filtering. The results table is bound to this function.

self.filteredUsers = ko.computed(function () {

    var textFilter = self.filter();          // this is an observable bound to a text field
    var checkedRoles = self.selectedRoles(); // this is a computed, which returns an array of checked roles

    return ko.utils.arrayFilter(self.users(), function (user) {

        var match = true;

        if (user.displayName.toLowerCase().indexOf(textFilter.toLowerCase()) == -1) {
            match = false;
        }

        // for each ticked role, check the user has the role
        for (var i = 0; i < checkedRoles.length; i++) {
            var roleMatch = false;
            for (var j = 0; j < user.roles().length; j++) {
                if (user.roles()[j].roleId === checkedRoles[i].roleId && user.roles()[j].granted()) {
                    roleMatch = true;
                    break;
                }
            }
            if (!roleMatch) {
                match = false;
            }
        }

        return match;
    });
});

1条回答
The star\"
2楼-- · 2019-07-31 18:09

I think that a good optimization would be creating a grantedRoles computed on your user object. This computed would return an object that you can use as an index, would contain properties keyed by a role's unique identifier and would only contain roles that are granted.

Then in filteredUsers, you would check the grantedRoles object against each checked role, rather than looping through user.roles() for each checked role.

查看更多
登录 后发表回答