0

I'm using Angular and what I'm trying to do is pass a filtered collection to a directive.

My view looks like this:

<h1>Filtered Collection (no directive)</h1>
<ul>
    <li ng-repeat="person in filteredPeople = (people | filter: { isChecked: true })">
        {{ person.name }}
    </li>
</ul>

The data that I'm passing is just a simple array of objects:

$scope.people = [
        {
            name: "George",
            isChecked: false
        },
        {
            name: "Jane",
            isChecked: false
        },
        {
            name: "Peter",
            isChecked: true
        }
    ];

Everything works fine so far. But when I try to put the HTML above in a directive the app crashes.

Directive:

myApp.directive('filterPeople', function () {
    return {
        restrict: 'A',
        template: '<h1>Filtered Collection (directive)</h1>' +
                     '<ul>' +
                      '<li ng-repeat="item in collection">{{ item.name }}</li>' +
                     '</ul>',
        scope: {
            collection: '=collection'
        }
    }
});

View:

<div filter-people collection="filteredPeople"></div>

The error that I get:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations

P.S. I've uploaded my example in Plunker and everything seems to work there. I've checked whether the code in my app is the same and it is. So, why is this happening? My code runs on Plunker but doesn't in my real app.

Yulian
  • 6,262
  • 10
  • 65
  • 92

1 Answers1

1

You are not actually filtering in the directive; you are passing the filtered data from your filterPeople statement into a directive for displaying. The app crashes because you have one ng-repeat affecting the output of another ng-repeat causing an infinite loop (Angular kills the cycle after 10 recursions)

Plunkr here shows the proper way to fix this. Simply pull your filter into your directive.

http://plnkr.co/edit/avjr306MESGE8xE6yN1M?p=preview

myApp.directive('filterPeople', function() {
  return {
        restrict: 'A',
        template: '<h1>Filtered Collection (directive)</h1>' +
                     '<ul>' +
                      '<li ng-repeat="item in collection | filter: { isChecked: true }">{{ item.name }}</li>' +
                     '</ul>',
        scope: {
            collection: '=collection'
        }
    }
});
Chris Story
  • 1,197
  • 1
  • 8
  • 17
  • Yes, you are right but I really want to work with the filtered data and not filter in the directive. Is this possible? – Yulian Oct 30 '15 at 12:45
  • @Yulian Don't use a directive to filter data. Use a [custom filter](http://toddmotto.com/everything-about-custom-filters-in-angular-js/) via the `$filter` service. – ryanyuyu Oct 30 '15 at 12:49
  • @Yulian So you just want to pass a filtered list based on UI inputs but create a directive for the purpose of displaying it? If that is the case, an ng-repeat for the purposes of getting filteredData is the wrong approach and you should use $filter to create a new $scope object that would be used by the directive. – Chris Story Oct 30 '15 at 12:51
  • Aha, I decided to use the collection in the UI because by writing it this way there's no need to add any logic in the controller but maybe you are right. So, initially I have the full collection. Then, the user should do some things in the UI (like checking these checkboxes) which will change the isChecked property of the objects. Then, he/she will use the filtered collection to input some other data. So, in this case you suggest using $filter and working with two separate collections instead of filtering in the UI of the directive and working with one and the same collection? – Yulian Oct 30 '15 at 13:04