Tip: accessing filtered array outside ng-repeat

August 11, 2014    tip

There is a common question I get time to time, so I decided to write about it here.

Let’s say I have an array of people and I filter them on a ng-repeat, then I want to access that filtered result to do something. I can imagine a few different use cases:

  • See how many items matched your search to recalculate your pagination.
  • Change the template based on the number of results you have.
  • Display a message if you got no results.
  • Show the quantity of items returned from your search.
  • Etc.

Let’s start with our controller:

app.controller('MainCtrl', function($scope) {
  $scope.people = ['fox', 'rosi', 'err3', 'rob', 'cesar', 'geoff'];
});

And then our html:

<body ng-controller="MainCtrl">
  <h2>List of people</h2>
  Search: <input type="text" ng-model="search">
  <ul>
    <li ng-repeat="person in people | filter:search">
      {{ person }}
    </li>
  </ul>
  
  Number of filtered people: {{people.length}}
</body>

Try it


Uhm, it doesn’t update the number of filtered people… It keeps saying 6 no matter what. Also, if there is no result, I wanted to see any message, at least to know that I got no results and it is not an error.

Ok, I hear you, but how can we achieve that? Well, we can’t do a length on the original array, it never changes. What if we create a new array that hold only the filtered people? Yeah, why not? We could create some method on our controller to do the filtering and then iterate over it with ng-repeat. That is fine but that defeats the purpose of our nifty HTML with no extra code for filtering. Can’t we do better? Yeah, let’s see:

<body ng-controller="MainCtrl">
  <h2>List of people</h2>
  Search: <input type="text" ng-model="search">
  <ul>
    <li ng-repeat="person in filteredPeople = (people | filter:search)">
      {{person}}
    </li>
  </ul>
  <p ng-hide="filteredPeople.length">There is no result</p>
  
  Number of filtered people: {{filteredPeople.length}}
</body>

What’s going on here? We are creating that new array directly on the HTML. The idea can be read as follow: Filter our people array, save the result on a new filteredPeople array and finally, iterate over it.

What’s the advantage here? That we can access our filteredPeople where we need it, both in our HTML and controller. Having that in mind, we can now access its length property to show a message if there are no results and even to count the number of filtered people.

But what are the disadvantage? filteredPeople is going to be evaluated in every $digest and on a big big list it can be problematic and then is when we should consider doing the filtering on the controller.

Try it


That is all. A nice trick for a really common problem that comes useful on small to medium lists.

comments powered by Disqus