I currently am working on some code with lazy-loaded content + controllers. My code basically works like this fiddle. However, for some reason, my version does not work, and instead I get an infinite digest cycle whenever angular tries to update it's view.
The issue disappears when I remove ng-include
from this simple repeat statement:
<div class="container" ng-repeat="pageName in pageNames">
<div ng-include="pageName"></div>
</div>
Weirdest part: The exact same error
occurs even if pageNames
is never assigned to the scope. Both scopes (the scope of outer and inner controller - I have each one) can be completely empty (I checked with Batarang - I only have two empty scopes), and I still get the error.
My code is a bit too involved, with too many other dependencies, so posting it here makes no sense. It's purest version is the fiddle above. I cannot find a difference in the logic between the two. When I inspect my scopes with Batarang, I don't see anything suspicious either:
$watch
ng-model
I conclude that I am not explicitly changing anything, so it must be something under the hood of angular.
Can I somehow get Angular or Batarang to tell me which scope variables have changed after a digest iteration, so I can identify the culprit causing the infinite loop?
UPDATE:
I finally figured out that history.pushState
messes everything up. I am now looking into alternatives, such as the $location
service. Nevertheless, I would still like to know how to debug this kind of issue in general. Any hints?
The best first step for debugging an infinite loop is to comment out different sections or lines of code, then running the program to see where the infinite loop is occurring. Start by testing any sections that contain for or while loops, then if/else statements, then other blocks of code.
Digest cycle is what Angular JS triggers when a value in the model or view is changed. The cycle sets off the watchers which then match the value of model and view to the newest value. Digest cycle automatically runs when the code encounters a directive.
The digest loop is responsible to update DOM elements with the changes made to the model as well as executing any registered watcher functions. The $digest loop is fired when the browser receives an event that can be managed by the angular context. This loop is made up of two smaller loops.
There's no direct way to get a list of scope variables that changed in each digest, but it's really simple to modify Angular's source to do that (temporarily) if you're in an emergency-debugging situation. The $digest code is here:
angular.js > src > ng > rootScope.js
If you look at this method you'll see references to a watchLog array. AngularJS uses this to report useful error messages when it detects looping like you're getting. You could easily add a few console.log items here, or maintain your own array that you could log later or inspect with your debugger. You can also set breakpoints here to see what is going on.
It's not clear to me why history.pushState() would have anything to do with this because your original question doesn't talk about the use of $location, history, or related functions. However, note that it is a VERY common problem with ngInclude to create infinite loops when paths aren't what you expect. A huge number of server-side (e.g. NodeJS) projects are configured to return the home/index page when a resource isn't found because they assume there's front-end routing going on. That's actually a huge problem because you end up embedding your home page in your home page, which then embeds another copy, etc. I see this kind of problem posted about once a week here on S/O.
A simple test for this is to alter your server to return an empty 404 for any resource not found. I always recommend this whenever somebody mentions "infinite loop" and any kind of asset loading operation: ui-router, ngRoute, ngInclude, etc.
This is particularly common when using relative URLs in paths.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With