Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to implement multiple filters AngularJS?

I have multiple filters working fine over an ng-repeat. However, the code seems unnecessarily long to actually action the filters on a set and I'm wondering if there is a better way.

Here is an example filter - this bit I'm OK with (unless anyone has any advice) - they all follow a similar structure:

app.js

.filter('taskClient', function() {
    return function (items, clientId) {
        if (!clientId) { return items; }
        var filtered = [];
        angular.forEach(items, function(item) {
            if (item.client) {
                if (item.client.id === clientId) {
                    filtered.push(item);
                }
            }
        });
        return filtered;
    }
})

And as I said - there are several of these. Then, on my ng-repeat, I implement them as so (this is the bit that seems hard to maintain and overly long and would appreciate info on any better techniques):

tasks-index.html

<md-list-item icp-task-line ng-repeat="task in TasksCtrl.tasks | taskOwner: TasksCtrl.selectedUserFilter | taskClient: TasksCtrl.clientId | taskDepartment: TasksCtrl.departmentId | taskPriority: TasksCtrl.priority | taskWithClient: TasksCtrl.withClient | taskEndDate: TasksCtrl.endDate | filter: {progress: 0} | filter: searchText | orderBy: TasksCtrl.orderTasks" class="md-2-line"></md-list-item>

Judging by how much scroll is involved here, I imagine you can see my issue with the above code. In order to get the length of tasks in view (also separated into completed, in progress etc) I have to list all the filters out again.

Is there a better way to implement these filters?

I'm also concerned that after reading this article I'm not understanding the difference between stateful and nonstateful filters - are the above filters optimised for performance?

like image 444
DJC Avatar asked Sep 07 '16 09:09

DJC


2 Answers

you can combine all your similar filters into one, for example (I made very approximate assumptions about structure of your task model :), I inserted filter: {progress: 0} filter inside as well):

.filter('taskFilter', function() {
  return function (items, args) {
    return items.filter(function(item) {
      return (!args.selectedUserFilter || item.selectedUserFilter === args.selectedUserFilter) &&
             (!args.clientId || item.client && item.client.id === args.clientId) &&
             (!args.departmentId || item.department && item.department.id === args.departmentId) &&
             (!args.priority || item.priority === args.priority) &&
             (!args.withClient || item.withClient === args.withClient) &&
             (!args.endDate || item.endDate === args.endDate) &&
             item.progress === 0;
    });
  }
})

now HTML may look like

<md-list-item icp-task-line ng-repeat="task in TasksCtrl.tasks | taskFilter: TasksCtrl | filter: searchText | orderBy: TasksCtrl.orderTasks" class="md-2-line"></md-list-item>
like image 188
Andriy Avatar answered Sep 24 '22 22:09

Andriy


I have two pieces of advice for you. First, if you want to get the filtered result in a variable so you don't have to apply all the filters again to get the count, you can add as filtered_result at the end of your filters and before any track by... as described in the documentation.

My other advice to you is to not force the use of filters in the HTML. It might be better in your case to create a function that filters your TasksCtrl.tasks once and saves the results off to a TasksCtrl.filteredTasks property which your ng-repeat can iterate over with way fewer watches. If you want to keep the filtering code in Angular filters, you can inject $filter and run them manually in your function.

function filterTasks(){
    var tasks = TasksCtrl.tasks;
    tasks = $filter('taskOwner')(tasks, TasksCtrl.selectedUserFilter);
    tasks = $filter('taskClient')(tasks, TasksCtrl.clientId);
    tasks = $filter('taskDepartment')(tasks, TasksCtrl.departmentId)
    tasks = $filter('taskPriority')(tasks, TasksCtrl.priority);
    tasks = $filter('taskWithClient')(tasks, TasksCtrl.withClient);
    tasks = $filter('taskEndDate')(tasks, TasksCtrl.endDate)
    tasks = $filter('filter')(tasks, {progress: 0});
    tasks = $filter('filter')(tasks, searchText);
    tasks = $filter('orderBy')(tasks, TasksCtrl.orderTasks);
    TasksCtrl.filteredTasks = tasks;
}
like image 39
adam0101 Avatar answered Sep 22 '22 22:09

adam0101