7

I want to call function in ng-repeat attribute, here is my code

example plnkr

html

<body ng-controller="mainCtrl">
  <div ng-repeat='item in getGroupedRange() track by item.id'>
    <span>{{item.val}}</span>
    <span>{{item.abs}}</span>
    <span>{{item.rel}}</span>
    <span>{{item.cum}}</span>
  </div>
</body>

js

$scope.getGroupedRange = function() {
    return [
      {
        val: 1,
        abs: 1,
        rel: 1,
        cum: 1,
        id: 123456
      }
    ];
  };

When I opened console I noticed the error

10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn: function (c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}","newVal":9,"oldVal":8}],[{"msg":"fn: function (c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}","newVal":10,"oldVal":9}],[{"msg":"fn: function (c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}","newVal":11,"oldVal":10}],[{"msg":"fn: function (c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}","newVal":12,"oldVal":11}],[{"msg":"fn: function (c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}","newVal":13,"oldVal":12}]]

The main goal of my code is using function in ng-repeat for calculating data in each event loop

3
  • Possible duplicate of How to Loop through items returned by a function with ng-repeat? Commented Nov 21, 2015 at 19:03
  • @Tristan hashKey doesn't work from your link. so.. Commented Nov 21, 2015 at 19:08
  • I believe that hashKey and track by are both related to how DOM rendering is handled (or not handled) for items in the repeat. The infinite digest issue is due to ngRepeat using $watchCollection internally which will pickup on the new object in your new array. Commented Jan 21, 2016 at 14:12

4 Answers 4

7

No, you can't use function in ngRepeat like this. The problem is that Angular uses strict comparison of objects in digest loop to determine if the value of the property changed since the last check. So what happens is that getGroupedRange returns new value (new array) each time it's called. Angular has no idea and considers this value as unstable and thus continues checking. But it aborts after 10 checks.

You need to construct necessary array and assign it to scope property, so it will not change during digest loop:

$scope.groupedRange = $scope.getGroupedRange();

then use it like in ngRepeat

  <div ng-repeat='item in groupedRange track by item.id'>
    <span>{{item.val}}</span>
    <span>{{item.abs}}</span>
    <span>{{item.rel}}</span>
    <span>{{item.cum}}</span>
  </div>
Sign up to request clarification or add additional context in comments.

7 Comments

so using track by doesn't make sense?
Why, track by solves other problem it's not related to this one.
Sure I mean only my case.
how ng detemine that getGroupedRange returns new value?
It compares previos value (from the previous check) with new one. getGroupedRange() returns new array every time.
|
0

Angular will do calculation and automatic showing in template on data change for you. But by putting ng-repeat='item in getGroupedRange() you put this into endles cycle recalculation.

Try to avoid this by assigning the value of the items (that may be changed by $scope.getGroupedRange function in any way) in the list to some scope variable, say $scope.range, that will be iterated in ng-repeat.

in controller

$scope.getGroupedRange = function() {
    $scope.range =  [
      {
        val: 1,
        abs: 1,
        rel: 1,
        cum: 1,
        id: 123456
      }
    ];
  };

in template

 <div ng-repeat='item in range track by item.id'>
    <span>{{item.val}}</span>
    <span>{{item.abs}}</span>
    <span>{{item.rel}}</span>
    <span>{{item.cum}}</span>
  </div>

4 Comments

When I should call the getGroupedRange function? I don't want paste getGroupedRange() in each place... thx
you call it when you need,
I don't want to repeat myself.
you only need track by if you have similar items in the array. it doesn't infulence the other part that we are solving here.
0

Found the solution:

  $scope.prev = null;
  $scope.getGroupedRange = function() {
    var data = [{
      val: 1,
      abs: 1,
      rel: 1,
      cum: 1,
      id: 123456
    }];
    if (angular.equals($scope.prev, data)) {
      return $scope.prev;
    }
    $scope.prev = data;
    return data;
  };

4 Comments

The discussion here was to explain you that using function in ng-repeat is a bad practice. But yes, you can always find a way shoot in your leg in angular world
@shershen Why do you think that using function in ng-repeat is a bad practice?
@shershen I do not think using ng-repeat on a function is bad practice at all. On the contrary, it's well aligned with functional programming. On the other hand, the discussion here shows how badly angular plays with FP.
how is mutating a member and returning it later functional? haha
0

Try doing something like this:

<body ng-controller="mainCtrl">
    <div ng-init="grpRange = getGroupedRange()">
        <div ng-repeat='item in grpRange track by item.id'>
            <span>{{item.val}}</span>
            <span>{{item.abs}}</span>
            <span>{{item.rel}}</span>
            <span>{{item.cum}}</span>
        </div>
    </div>
</body>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.