Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: $rootScope:infdig error when calling a ng-style function inside ng-repeat

I'm trying to build an animation on some phrases that will be displayed on the site main page, in a random position and with fade and translate effects.

I would achieve this using ng-style attribute inside an ng-repeat attribute and setting the ng-style value calling a JavaScript function defined inside the HomeController.

Using this approch cause angular to throw the exception: $rootScope:infdig error 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations

I read so much about this but no solution has solved my case. Anyone could help me?

Here is a part of index.html:

<div class="phrasesContainer" animate-phrases="">
      <h3 class="flying-text" ng-repeat="phrase in Phrases" ng-style="getTopLeftPosition()">{{phrase}}</h3>
    </div>

Here is the controller function:

$scope.getTopLeftPosition = function() {
var top = randomBetween(10, 90);
var left = getRandomTwoRange(5, 30, 70, 80);

return {
  top: top + '%',
  left: left + '%'
};

}

Here is a demo: http://plnkr.co/edit/8sYks589agtLbZCGJ08B?p=preview

like image 707
Androidian Avatar asked Sep 22 '15 16:09

Androidian


2 Answers

@Hieu Le hinted to the problem, but your issue is that since you are always returning a random position in your getTopLeftPosition function, angularjs digest loop will get called every time to actually propagate the changes to the watchers. This caused it to keep running over and over.

What you can do is to pre-calculate your random positions and then use that in your html.

For example, in your activate function you can do something like this:

  function activate() {
    $scope.Phrases = ["Phrase 1", "Phrase 2", "Phrase 3", "Phrase 4", "Phrase 5", "Phrase 6"];
    $scope.PhrasesAndPositions = $scope.Phrases.map(function(phrase){
      return {
        phrase: phrase,
        position: getTopLeftPosition()
      }
    });
  }

And then you can change your html to something like this:

    <div class="phrasesContainer" animate-phrases="">
      <h3 class="flying-text" ng-repeat="pap in PhrasesAndPositions" ng-style="pap.position">{{pap.phrase}}</h3>
    </div>

Here is the working plunk with my changes: http://plnkr.co/edit/FD9hYX9Q5wUkW2q7y86M?p=preview

like image 58
JoseM Avatar answered Nov 15 '22 03:11

JoseM


Here's a solution where I moved your style generation into the directive. The position is being set right before showing the element. Since this is a CSS change, I modified the styling as well so that the position does not transition.

Here's the directive. The code I've excluded has not been changed:

app.directive('animatePhrases', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      setTimeout(function() {
        ...
      }, 1000);

      function changeText() {
        var currentActive = $('.phrasesContainer .active');
        var nextActive = currentActive.next();
        currentActive.toggleClass('active');

        if (nextActive.length == 0)
          nextActive = $('.phrasesContainer .flying-text').first();

        nextActive.css(getTopLeftPosition()); // Add this
        nextActive.toggleClass('active');

        setTimeout(changeText, 5000);
      }

      function getTopLeftPosition() {
        ...
      }

      function getRandomTwoRange(firstStart, firstEnd, secondStart, secondEnd) {
        ...
      }

      function randomBetween(min, max) {
        ...
      }
    }
  };
});

CSS:

.flying-text {
    transition: opacity 2s ease-in-out, margin 2s ease-in-out;
    position: absolute;
    opacity: 0;
    font-size: 1.5em;
}

In your HTML, simply remove the ng-style.

Plunker: http://plnkr.co/edit/bZB3A5hD7Bc4r4pp1g7V?p=preview

like image 34
Anid Monsur Avatar answered Nov 15 '22 05:11

Anid Monsur