Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS InfDig error (infinite loop) with ng-repeat function that returns array of objects

Here's my code:

<h1 ng-repeat="item in func()">something</h1>

$scope.func = function(){
  return [{"property" : "value1"},{"property": "value2"}];
}

In Angular.js v. 1.1.1 there's no mistake. In Angular.JS v 1.2.1 I get an infDig mistake.

Fiddle of v.1.1.1

Fiddle of v.1.2.1

Could you explain this situation? Thanks a lot.

like image 600
user3162402 Avatar asked Jan 05 '14 11:01

user3162402


People also ask

Why is NG-repeat not working?

Solution 1 There are two mistakes in your code: In your table, you have to wrap the properties between {{ and }}, for example {{employee. Fname}} instead of just employee. Fname .

What is Ng-repeat in AngularJS?

AngularJS ng-repeat Directive The ng-repeat directive repeats a set of HTML, a given number of times. The set of HTML will be repeated once per item in a collection. The collection must be an array or an object. Note: Each instance of the repetition is given its own scope, which consist of the current item.

How do you pass an index in NG-repeat?

Each ng-repeat creates a child scope with the passed data, and also adds an additional $index variable in that scope. So what you need to do is reach up to the parent scope, and use that $index . Save this answer.

How do you use two ng-repeat in a table?

NEST TWO ng-repeatThe first ng-repeat in the tr tag will create the rows and the second one in the td tag will create one column per element in the collection. By using one ng-repeat for the row and an other for the column, the rows and the columns of your table is now driven by a subset.


1 Answers

As of AngularJS 1.2: The "track by" expression was added to ng-repeat and more appropriately addresses this issue as demonstrated in the following code.

<h1 ng-repeat="item in func() track by $index">something</h1>

$scope.func = function(){
  return [{"property" : "value1"},{"property": "value2"}];
}

The following article helps understand the expression in more detail and why it is so useful, particularly when dealing with $$haskey Using Track-By With ngRepeat In AngularJS 1.2 by Ben Nadal.

The problem is that you're creating a new array each time, so it's something new that angular needs to track. As far as I can tell, ng-repeat runs, then immediately checks its collection again to see if anything changed in that cycle. Because the function returns a new array, that is perceived as a change.

Check this out: http://jsfiddle.net/kL5YZ/. If you look in the console.log and click the button, you will see that the $$hashKey property of the objects is being changed each time ng-repeat runs.

The change occurs starting at version 1.1.4, but the changelog doesn't give any clues as to why the behavior is different. The new behavior does make more sense to me.

Here's a great post I found explaining the current behavior in depth: How to Loop through items returned by a function with ng-repeat?

If you make sure to return the same object/array each time, you won't have the error. You could have the function cache anything it creates based on the arguments and always return the same array/object when those arguments are passed in. So, myFunc('foo') will always return the same array, not a new one that looks the same. See the notes in my code below. Live demo (click).

<div ng-repeat="foo in foos">
  <div ng-repeat="bar in barFunc(foo)">{{bar.text}}</div>
  <div ng-repeat="bar in barFunc('test')">{{bar.text}}</div>
</div>

JavaScript:

var app = angular.module('myApp', []);

app.controller('myCtrl', function($scope, myService) {
  $scope.foos = [
    'a','b','c'
  ];
  //I put this into a service to avoid cluttering the controller
  $scope.barFunc = myService.getObj;
});

app.factory('myService', function() {
  /*
   * anything created will be stored in this cache object,
   * based on the arguments passed to `getObj`.
   * If you need multiple arguments, you could form them into a string,
   * and use that as the cache key
   * since there's only one argument here, I'll just use that
   */
  var cache = {};

  var myService = {
    getObj : function(val) {
      //if we haven't created an array with this argument before
      if (!cache[val]) {
        //create one and store it in the cache with that argument as the key
        cache[val] = [
          {text:val}
        ];
      }
      //return the cached array
      return cache[val];
    }
  };

  return myService;
});
like image 76
m59 Avatar answered Nov 15 '22 22:11

m59