Reputation: 2584
I have a $scope.users
that I use to show a table and I need to apply a filter with multiple options,
Here is the $scope.users
object:
[
{
"login": "[email protected]",
"merchant": {
"merchantId": 500095,
"status": "ENABLED",
"defaultAccountId": null,
"accounts": {
"500092": "ADMIN",
"500178": "CONSULT",
"500180": "ADMIN",
"500182": "ADMIN",
"500201": "ADMIN"
}
},
"name": "Carlos"
},
{
"login": "[email protected]",
"merchant": {
"merchantId": 500095,
"status": "DISABLED",
"defaultAccountId": null,
"accounts": {
"500092": "CONSULT",
"500178": "MANAGER",
"500180": "ADMIN",
"500182": "ADMIN",
"500201": "ADMIN"
}
},
"name": "Carlos Andres"
},
{
"login": "[email protected]",
"merchant": {
"merchantId": 500095,
"status": "DISABLED",
"defaultAccountId": null,
"accounts": {
"500092": "CONSULT",
"500178": "MANAGER",
"500180": "ADMIN",
"500182": "ADMIN",
"500201": "ADMIN"
}
},
"name": "Cosme"
}
]
In the filter I pass other object from the $scope
to filter the data, in this case is $scope.searchParams
that I previously calculate in a directive.
$scope.searchParams
:
[
{'name':'Cosme'},
{'login':'[email protected]'},
{'status':'ENABLED'},
{'status':'DISABLED'},
{'account':'267123'},
{'account':'543987'},
{'account':'544111'},
{'account':'543987'}
]
As you can see, name
and login
are unique in the filter, but status
and account
can have multiple values to apply in the filter. Here is my approach to do that but I think is very ineffective, also is not complete because doesn't have the multiple filter functionality:
var secUser = function () {
return function (input, list) {
var out = [];
var nameArr = [],
loginArr = [],
statusArr = [],
profileArr = [],
accountsArr = [];
angular.forEach(list, function(filter){
console.log(filter);
if(filter.hasOwnProperty('name')){
nameArr.push(filter.name);
}
if(filter.hasOwnProperty('login')){
loginArr.push(filter.login);
}
if(filter.hasOwnProperty('status')){
statusArr.push(filter.status);
}
if(filter.hasOwnProperty('account')){
accountsArr.push(filter.account);
}
if(filter.hasOwnProperty('profile')){
profileArr.push(filter.profile);
}
});
angular.forEach(input, function(user){
// no filters applied
if(nameArr.length < 1 && loginArr.length < 1 &&
statusArr.length < 1 && accountsArr.length < 1 &&
profileArr.length < 1){
out.push(user);
}
// filters to be applied
else{
if(nameArr.indexOf(user.name) > -1){
out.push(user);
}
if(loginArr.indexOf(user.login) > -1){
out.push(user);
}
if(statusArr.indexOf(user.merchant.status) > -1){
out.push(user);
}
}
});
return out;
};
};
//-- register filter
app.filter('secUser', secUser);
Finally here is how I am using the fiter: <tbody ng-repeat="user in users | secUser:searchParams " class="table-striped">
¿Is there a better way to implement this filter considering that in the future there can be more filter fields? Also I think is very coupled with the view.
Upvotes: 4
Views: 507
Reputation: 5873
There are multiple ways you could do this but one requirement is you need to make your searchParams
structure more generic. For example, here I'm using an object where each key is the path to the target object and the corresponding value is an array with all the values you want to match against:
$scope.searchParams = {
'name': ['Cosme'],
'login': ['[email protected]'],
'merchant.status' : ['ENABLED','DISABLED'],
'merchant.accounts' : ['267123','543987 ','544111','543987'],
}
Then you can update your filter to handle generic data. It can't be completely generic because it does depend on your business logic, e.g. does accounts
match on the key 500092
or the value CONSULT
. Here is an updated filter function handling the three kinds of targets in your data:
name
status
accounts
JsFiddle (with data slightly modified for testing)
var secUser = function () {
return function (users, filters) {
return users.filter(function(user){
var match = false;
angular.forEach(filters, function(filterValues, keyPath){
if (match) return; // if we already found a match, don't continue searching
// get the target based on the contents of the keypath
var target = keyPath.split('.').reduce(function(object, key) {
// get the next part of the path, if exists
return object && object[key];
}, user); // initial value is the user object
if (target !== undefined){ // if the target exists for this item
var values = []; // stores the values from the item to search against
if(typeof target === "object") {
values = Object.keys(target);
}
else if (target.isArray) {
values = target;
} else {
values = [target];
}
// check if any entry in values matches any entry of the filter values
match = values.some(function (v) {
return filterValues.indexOf(v) >= 0;
});
}
});
return match;
});
};
};
Upvotes: 1