12

I have an array of objects that I'm displaying in my Angular app using ng-repeat. I'm filtering out items using filter and the value of a search input. It works as expected. But, I have a "select all" / "deselect all" option and I only want to select the visibile items in the list (the ones that meet the current search criteria).

Without performing the same logic in my controller (i.e. using indexOf the search value on each of my objects), how can I tell which items are currently filtered out by ng-repeat/filter?

My view:

<input type="text" ng-model="searchValue">
<input type="checkbox" ng-model="checkAll" ng-change="toggleAll()">

<tr ng-repeat="item in items | filter:searchValue">
    <td>{{item.id}}</td>
    <td>{{item.name}}</td>
</tr>

A function in my controller:

$scope.toggleAll() {
     for(var i in $scope.items){
         // how can I tell if this item is filtered out in the view?
     }
}

I have significantly simplified my code samples here for simplicity since this question doesn't need much more detail. Is there a way to do what I'm thinking or do I need to perform the "search" again?

amlyhamm
  • 1,110
  • 2
  • 18
  • 36
  • Check out @azium 's comment on my answer. You'll probably want to use [angular.forEach](https://docs.angularjs.org/api/ng/function/angular.forEach) – allienx Jun 08 '15 at 20:40
  • Of course, I was just throwing it out there as a quick example! Thanks. – amlyhamm Jun 09 '15 at 12:37

3 Answers3

20

You can bind the filtered array to another scope variable in your view, then access that in your controller.

View:

<tr ng-repeat="item in filteredItems = (items | filter:searchValue)">
  ...
</tr>

Controller:

$scope.toggleAll = function () {
  angular.forEach($scope.filteredItems, function (item) {
    // do stuff
  })
}
azium
  • 20,056
  • 7
  • 57
  • 79
  • I've never seen this feature before, it's awesome! Do you know when they added it? – mikeswright49 Jun 08 '15 at 20:42
  • 1
    I don't, but I have some 1.27 code that uses it.. so it's been out for a while. – azium Jun 08 '15 at 20:43
  • This is awesome - exactly what I was hoping for. Thank you! – amlyhamm Jun 09 '15 at 12:38
  • 1
    works great, unfortunately it is not supported by angular utils dir-paginate incase anyone else stumbles across this and has the same issues – pcnate Aug 18 '16 at 13:20
  • @pcnate do you have any solutions for dir-paginate? – Rocky Aug 26 '16 at 07:45
  • I don't have access to the code today, but I basically left the dir paginate alone. I added something similar to this where I wanted access to the same items `{{ ( vm.myItems | filter1 | filter2 | sorting | dirPaginationPageFilter ).length }}` to get the number of items in my view. For in the controller it was a bit more complicated but I used nested `$filters` to do the same thing. I'll post an answer on Monday giving more details about what I did for others to benefit from. – pcnate Aug 26 '16 at 14:43
1

Your issue is that ng-repeat is scope isolated. As a result you can't refer to the internal list that is being managed by ng-repeat from your controller/directive.

As a result there are 2 options

  1. Bind the filtered list to ng-repeat from your controller/directive, so you maintain the filtered list.

    //in your controller
    $scope.filteredItems = $filter('yourFilter')($scope.items,$scope.searchText);
    $scope.$watch('searchText', function(newValue){
       $scope.filteredItems = $filter('yourFilter')($scope.items, newValue);
    });
    
    //in your view
    <tr ng-repeat="item in filteredItems">
        <td>{{item.id}}</td>
        <td>{{item.name}}</td>
    </tr>
    
  2. Perform the filter again in your controller/directive

    $scope.toggleAll() {
        var items = $filter('yourFilter')($scope.items, $scope.searchText);
        for(var i in items){
           //set your selected property
        }
    }
    
mikeswright49
  • 3,381
  • 19
  • 22
1

Angular filters create a new array. So in order to perform an action on the filtered items you're going to have to capture the new array.

Something like:

$scope.toggleAll() {
  var filteredItems = $filter('filter')($scope.items, $scope.searchValue);
  for(var i in filteredItems) {
    ...
  }
}

If you don't want to filter twice you'll have to filter the array yourself every time searchValue changes and ng-repeat over that filtered array, ng-change is useful in that case.

allienx
  • 877
  • 6
  • 10
  • 1
    Don't use `for..in` for arrays! It doesn't do what you want. http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea – azium Jun 08 '15 at 20:37
  • 1
    @azium right you are. I simply copy and pasted the OP's snippet. – allienx Jun 08 '15 at 20:38
  • Oh woops. it's the end of the day, I swear! Thanks for notifying OP =D – azium Jun 08 '15 at 20:49