view raw
Horen Horen - 6 months ago 32
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">
<li ng-repeat="user in (filteredUsers = (users | orderBy:order:reverse | filter:filter.user ) | limitTo: filter.limit)" ng-click="showDetails(user)">
{{}} / {{user.firstname}} {{user.lastname}}
<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
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!


Here are the controller and the resource.


function ($scope, Users) {
function init(){
$scope.users = Users.get();

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:

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


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:

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:

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.