Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 238
Horen Avatar asked Apr 08 '14 04:04

Horen


People also ask

What are the built-in directives in angular?

AngularJS comes with a set of these directives built-in, like ngBind, ngModel, and ngClass. Much like you create controllers and services, you can create your own directives for AngularJS to use. When AngularJS bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements.

What happens when a scope is destroyed in angular?

Just before a scope is destroyed, a $destroyevent is broadcasted on this scope. Application code can register a $destroyevent handler that will give it a chance to perform any necessary cleanup. Note that, in AngularJS, there is also a $destroyjQuery event, which can be used to clean up DOM bindings before an element is removed from the DOM.

Should I prefix my own directives with Ngor?

Similarly, do not prefix your own directives with ngor they might conflict with directives included in a future version of AngularJS. For the following examples, we'll use the prefix my(e.g. myCustomer). Template-expanding directive Let's say you have a chunk of your template that represents a customer's information.

How to access scope variables from the templateurlfunction in AngularJS?

AngularJS will call the templateUrlfunction with two parameters: the element that the directive was called on, and an attrobject associated with that element. Note:You do not currently have the ability to access scope variables from the templateUrlfunction, since the template is requested before the scope is initialized.


1 Answers

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.

like image 123
Barnash Avatar answered Oct 07 '22 12:10

Barnash