Horen Horen - 24 days ago 6
AngularJS Question

angularjs directive: $rootScope:infdig error

I'm trying to build a pagination directive with angularjs 1.2.15:

This is my view:

<input type="text" ng-model="filter.user">
<input type="number" ng-model="filter.limit" ng-init="filter.limit=5">
<ul>
<li ng-repeat="user in (filteredUsers = (users | orderBy:order:reverse | filter:filter.user ) | limitTo: filter.limit)" ng-click="showDetails(user)">
{{user.id}} / {{user.firstname}} {{user.lastname}}
</li>
</ul>
<div pagination data='filteredUsers' limit='filter.limit'></div>


and here is my pagination directive:

app.directive('pagination', function(){
return {
restrict: 'A',
templateUrl: 'partials/pagination.html',
scope: {
data: '=data',
limit: '=limit'
}
}
})


Everything works perfectly fine without the pagination directive. However with my new directive as soon as I load the page I get a
$rootScope:infdig
error which I don't understand since the directive is not doing anything to manipulate data that could end up in an infinite loop.

What is the problem here and how can I solve it? Thanks!

Update:

Here are the controller and the resource.

Controller:

usersModule.controller('usersController',
function ($scope, Users) {
function init(){
$scope.users = Users.get();
}
init();
})


Resource (gets users as an array from a REST API):

app.factory('Users', function($resource) {
return $resource('http://myrestapi.tld/users', null,{
'get': { method:'GET', isArray: true, cache: true }
});
});


Update 2

Here is a demo: http://plnkr.co/edit/9GCE3Kzf21a7l10GFPmy?p=preview

Just type in a letter (e.g. "f") into the left input.

Answer

The problem is not within the directive, it's within the $watch the directive creates. When you send filteredUsers to the directive, the directive creates the following line:

$scope.$watch("filteredUsers", function() {
    // Directive Code...
});

Notice in the following example how we reproduce it without a directive: http://plnkr.co/edit/uRj19PyXkvnLNyh5iY0j

The reason it happens is because you are changing filteredUsers every time a digest runs (since you put the assignment in the ng-repeat statement).

To fix this you might consider watching and filtering the array in the controller with the extra parameter 'true' for the $watch:

$scope.$watch("users | orderBy:order:reverse | filter:filter.user", function(newVal) {
    $scope.filteredUsers = newVal;
}, true);

You can check the solution here: http://plnkr.co/edit/stxqBtzLsGEXmsrv3Gp6

the $watch without the extra parameter (true) will do a simple comparison to the object, and since you create a new array in every digest loop, the object will always be different. When you're passing the true parameter to the $watch function, it means it will actually do deep comparison to the object that returns before running the $watch again, so even if you have different instances of arrays that has the same data it will consider them equal.