AngularJS 'ng-filter' is very slow on array of ~1000 elements AngularJS 'ng-filter' is very slow on array of ~1000 elements angularjs angularjs

AngularJS 'ng-filter' is very slow on array of ~1000 elements


The main problem with the filter approach is that upon each change the dom is manipulated, so it's not the filter that's slow but the consequences. An alternative is to use something like:

ng-show="([item] | filter:searchFilter).length > 0"

on the repeated element.

Lending some code from @OverZealous, you can use the following to compare the behaviour:


Update: With Angular v1.2 came the track by syntax. Which also helps with such problems. Provided the elements have some unique attribute, one can use:

ng-repeat="item in items | filter:searchFilter track by item.id"

Where item.id has to be unique across all items. With track by only those dom-elements will be removed which are no longer the in the final list, others will be remembered. Whereas without track by the whole list is redrawn everytime. In short: much less dom manipulation = quicker redraw.


Another interesting optimization is to 'not trigger' the model change until a certain time.

Adding this to your search input field: ng-model-options="{debounce: 500}"

This will trigger the filter if the user stop typing during 500ms.

I updated the above fiddle:

http://jsfiddle.net/CXBN4/14/

<input ng-model="searchFilter" type="text" ng-model-options="{debounce: 500}" />


I created a fiddle to simulate (part of) the code you are showing.

On my computer, which is fast but not super fast, that runs acceptably well. It's a little slow, but it's filtering an overly long list that has two-way binding with the checkboxes. Every time you type a letter, the entire list must be scanned and items removed (or added).

I think your best bet for solving this is to add some sort of simple pagination, as shown in this StackOverflow answer.

Here I've modified my example to include pagination. You will probably want to invest in a better solution than just Next / Previous, but this shows you how the result can be extremely fast if you aren't showing everything all at once. It still searches the whole list, but the rendered list is much more limited.

The additions:

Add paging info to the scope in the controller:

$scope.currentPage = 0;$scope.pageSize = 20;$scope.numberOfPages = function () {    return Math.ceil($scope.items.length / $scope.pageSize);}

Create a new filter to start from a specific page:

app.filter('startFrom', function () {    return function (input, start, pageSize) {        start = +start; //parse to int        pageSize = +pageSize;        while (start > input.length) {            start -= pageSize;        }        if (start < 0) {            start = 0;        }        return input.slice(start);    };});

Add filters to the view to limit the list:

<li ng-repeat="value in items | filter:searchFilter |        startFrom:currentPage*pageSize:pageSize | limitTo:pageSize">

Add pagination buttons to the page:

    <div>        <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button> {{ currentPage+1 }}/{{ numberOfPages() }}        <button ng-disabled="currentPage >= items.length/pageSize - 1" ng-click="currentPage=currentPage+1">Next</button>    </div>