Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

$location runs into infinite digest under certain circumstances #1417

Closed
IgorMinar opened this issue Sep 25, 2012 · 86 comments
Closed

$location runs into infinite digest under certain circumstances #1417

IgorMinar opened this issue Sep 25, 2012 · 86 comments

Comments

@IgorMinar
Copy link
Contributor

when adding Angular to an existing project that makes use of html5 history. Whenever the url is changed outside of Angular, the next $digest to run produces this error:

Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: $locationWatch; newVal: 8; oldVal: 7"],["fn: $locationWatch; newVal: 9; oldVal: 8"],["fn: $locationWatch; newVal: 10; oldVal: 9"],["fn: $locationWatch; newVal: 11; oldVal: 10"],["fn: $locationWatch; newVal: 12; oldVal: 11"]]

I have my own routing in place, using goog.history.Html5History. I was trying to include some Angular stuff on a single page (works), but when the page changes, the first subsequent $apply produces the above error. It looks like the $location service is getting out of sync with the browser.

$locationWatch expects the $location service to be updated first, followed by the browser. But when I pushState to the browser directly, the $location service still has the old url.

The sad thing is that $location is not being used directly by this app, nor is $route. It's ngInclude, that depends on $anchorScroll, which depends on $location - maybe fixing this dependency should be a separate issue... (to be investigated)

@dbinit
Copy link
Contributor

dbinit commented Sep 25, 2012

Here is a jsFiddle that reproduces the issue: http://jsfiddle.net/dbinit/tMmKf/

@martinstein
Copy link

I also ran into this bug recently. For me it happened while testing in IE8 with html5Mode(true) (which in IE8 falls back to html5Mode(false), of course).

@kstep
Copy link
Contributor

kstep commented Nov 2, 2012

I confirm this bug for AngularJS 1.1.0 and IE9.

@fr0
Copy link

fr0 commented Nov 8, 2012

This is also happening to me in Chrome (Version 22.0.1229.94 m), when using window.history.pushState in response to a button click. Angular version: 1.0.2.

@gaiottino
Copy link

Any update to this? Giving us some IE9 headaches.

@scottweinert
Copy link

I am also experiencing this in IE9. Any update?

@ThomasDeutsch
Copy link

having the same problem. Still no fix for this?

@scottweinert
Copy link

For what it's worth, I was having this issue in IE9 and Android and here is what I did:

  1. Make sure you have a route to '/' in Angular and that it works properly - for example: $routeProvider.when('/', {templateUrl: 'partials/home', controller: HomeCtrl});

  2. Add $locationProvider.html5Mode(true).hashPrefix('!'); to your app configuration

  3. I had to add the following code to get android to work and I just added it in a script tag before angular loads etc.

var buggyAndroid = parseInt((/android (\d+)/.exec(window.navigator.userAgent.toLowerCase()) || [])[1], 10) < 4;
var buggyAndroid = parseInt((/android (\d+)/.exec(window.navigator.userAgent.toLowerCase()) || [])[1], 10) < 4;
if (!history.pushState || buggyAndroid) {
if (window.location.hash) {
if(window.location.pathname !== '/') window.location.replace('/#!' + window.location.hash.substr(2)); //Hash and a path, just keep the hash (redirect)
} else {
if (window.location.pathname === '/') window.location.replace('/#!/'); //No hash, no path
else window.location.replace('/#!' + window.location.pathname); //No hash, but have a path, take path
}
}

@dbinit
Copy link
Contributor

dbinit commented Nov 26, 2012

In my case, I wasn't using $location at all. It was just getting pulled in by $anchorScroll, which is used by ngInclude.

I wasn't using $anchorScroll either, so I just did this to remove the dependency:

angular.module('myApp', []).value('$anchorScroll', null);

If you're using $anchorScroll you could probably just null out $location instead.

@pgte
Copy link

pgte commented Dec 12, 2012

I'm running into this and I don't have explicit watchers and I don't import $location.
When I do history.pushState(...) I get the following error:

Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: function (){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=\n!1;return o}; newVal: 8; oldVal: 7"],["fn: function (){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=\n!1;return o}; newVal: 9; oldVal: 8"],["fn: function (){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=\n!1;return o}; newVal: 10; oldVal: 9"],["fn: function (){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=\n!1;return o}; newVal: 11; oldVal: 10"],["fn: function (){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace=\n!1;return o}; newVal: 12; oldVal: 11"]]
    at Error (<anonymous>)
    at Object.e.$digest (http://localhost:8080/js/lib/angular.min.js:85:217)
    at Object.e.$apply (http://localhost:8080/js/lib/angular.min.js:86:469)
    at HTMLAnchorElement.<anonymous> (http://localhost:8080/js/lib/angular.min.js:140:507)
    at HTMLAnchorElement.v.event.dispatch (http://localhost:8080/js/lib/jquery-1.8.3.min.js:2:38053)
    at HTMLAnchorElement.o.handle.u (http://localhost:8080/js/lib/jquery-1.8.3.min.js:2:33916) 

Any ideas?

@brandonsalmon
Copy link

Here is an example where the initial page load fails and reloads after throwing the above error.
http://jsfiddle.net/v7HG5/ (Open in IE)

Note: jsfiddle doesn't handle any angular routing, so the rendered view is a blank jsfiddle. Also, nothing will happen at all on an html5 supported browser.

My understanding is that the page is going to need to refresh to convert the url from html5 mode to hash mode (adding the "#" http://jsfiddle.net/#/v7HG5/). In my case this is fine, but it should handle its errors.

@ThomasBorghs
Copy link

Also problem with html5Mode(true) and IE8. Everything works in Chrome/FF, but in IE Angular is unable to bootstrap because the $locationWatcher keeps getting a new value, and thus $digest fails. This results in a redirection loop (for reasons not totally clear to me) where IE keeps redirecting to the "index" page and Angular fails to bootstrap.

Any news on this issue?

Right now the team is contemplating setting html5Mode(false) and rewriting the routing on the server side.

@gaiottino
Copy link

I've managed to reduce the occurance of this bug significantly by using $location.path() and $location.search() instead of manually using location.href. Hope that helps someone else.

@leonzinger
Copy link

@gaiottino were you using angular for routing or an external plugin? because i would love to see the solution for app that uses an external plugin but want to watch the url change

@gaiottino
Copy link

@leonzinger I was iterating an app which relied on crossroads.js and Handlebars to Angular. It worked great for Chrome/FF/Safari, but IE would have $digest problems described above. I've removed crossroads.js now in favor of using Angular for routing but with a patch I'm hoping will make it's way into master: #1901

@leonzinger
Copy link

@gaiottino Thanks a lot for the answer, i would love to see it working with an outside routing library though...

@gaiottino
Copy link

@leonzinger the only part which took some investigation was how to compile Angular specific code. Once you render html with Angular markup you need to compile it. I don't have any code to show but using https://github.com/leshill/handlebars_assets it was basically

template = HandlebarsTemplates[template_name]
html = template()
$(selector).append(html)
compile(selector) if html.has('ng-controller')

Where the compile function is

compile: (selector) ->
  app = angular.element(document)
  compiler = app.injector().get('$compile')
  scope = app.scope()

  elements = angular.element("#{selector} [ng-controller]")
  if elements.exists()
    for el in elements
      element = angular.element(el)
      template = compiler(element)
      html = template(scope)
    scope.$apply() if(!scope.$$phase)

If you plan to render html dynamically you should also destroy any previous controllers that are no longer used

destroy: (selector) ->
  elements = $(selector).find('[ng-controller]')
  for element in elements
    app = angular.element(element)
    if app
      scope = app.scope()
      scope.$destroy() if scope

The app was bootstrapped manually using

$ ->
    angular.bootstrap(document)

@rbygrave
Copy link

In case it is useful... I was hitting the "10 $digest() iterations reached. Aborting!" error when using $window.history.back(); with IE9 (works fine in other browsers of course).

I got it to work by using:

setTimeout(function() {
  $window.history.back();
},100);

@thebigredgeek
Copy link
Contributor

Thats not a very good solution though. It isn't guaranteed to work on slower machines. I have ran into a similar issue where I get the digest iteration error on some phonegap devices but not others. Quite bizare. Looking for a work around

@Vbahole
Copy link

Vbahole commented Feb 27, 2013

I'm seeing this as well. Using IE9 and I'm not even using history. I just use $location.search()['searchTerm'] in one of my controllers so that i can capture something from the query string and act on it. Works fine in ff with html5mode=true. in ie9 with mode true it spins then goes to the IIS home page and puts a # in my url. With mode set to false it doesn't perform the qs search.

@mezezo
Copy link

mezezo commented Feb 28, 2013

I am seeing this on IE9 with angular 1.0.5. Is this by any chance fixed on 1.1.3? Or is there a known workaround?

@BrainCrumbz
Copy link
Contributor

We are experiencing the same error on IE9 when invoking $window.history.back(). We tried with plain "window" and it's the same. At this fiddle http://jsfiddle.net/DGbNp/ you can see the first two functions from the Angular error "stack trace" from IE console error. (We edited the output to remove double quotes and \n 's).
For now we could live with dropping completely the "back button", but it's not gonna last long.
If anyone needs more info, please just ask.

@sahglie
Copy link

sahglie commented Mar 8, 2013

I'm also seeing this on IE9 with angular 1.0.5. Sadly this is a show stopper for our team as far as moving ahead with angular. :-(

@craftgear
Copy link

Bump, I have the exactly same thing on FF16 and Chrome22 with Angular1.0.5.
This is really a pain in ass.

@klebba
Copy link

klebba commented Apr 1, 2013

+1 - 1.1.3 / IE9

@florianorben
Copy link

Had the same issue recently, manually commenting out 577 - 611 in https://github.com/angular/angular.js/blob/master/src/ng/location.js did do the trick for me...
Obviously this isnt the best solution as modifiying angular's source isn't what should be done actually...

What about adding an option to dis-/enable $location's watch on browser url completely.. something like:

angular.module('myApp', []).config('$locationProvider', function($locationProvider) {
    $locationProvider.disable(); //disables/removes all watchers on browser url change
})

@cyberwombat
Copy link

+1 IE8/9

@glebm
Copy link

glebm commented Apr 25, 2013

$locationProvider.disable() would be a very useful addition.
Is there a way to remove a listener from scope?

@swlasse
Copy link

swlasse commented Jul 1, 2014

@lord2800 Thanks for mentioning that - I have struggled with the exact same issue for hours now. In my case, I have two apps bootstrapped using ng.bootstrap(). My workaround has been to skip using $location in one of the apps and use window.location as fallback instead.

Running angular v1.2.16, Chrome 35.0.1916.153.

@lord2800
Copy link

lord2800 commented Jul 1, 2014

@swlasse that's exactly what we ended up doing as well. Glad to be of help!

@almaron
Copy link

almaron commented Jul 2, 2014

This watch generates the same error on pageload. It's a simple pagination watch on a forum topic show page.
$window.history.pushState is the trigger, though it is only called once (I've checked it with console.log). any ideas?

$scope.$watch("postPagination.cur", function(newVal, oldVal) {
  if (angular.isDefined(newVal) && newVal && (newVal !== oldVal)) {
    $window.history.pushState({
      page: newVal,
      prev: oldVal
    }, "", $scope.currentPath + "?page=" + newVal);
    $scope.loadPosts(newVal);
  }
});

@sgarbesi
Copy link

Okay, I couldn't find an exact answer, so this is what worked for me (hopefully it helps someone else).

This is occurring for me in AngularJS v1.2.22.

In IE8 / IE9 I get the $rootScope:infdig from using window.location = 'XYZ'; to change the URL. It works fine in Chrome/FF/Safari, just not IE.

The solution for me was to simply use $location.

Ex:

Instead of:

window.location = '/#!/test123/';

Use:

$location.path('test123').replace();

@MaestroJurko
Copy link

I have this issue with the 1.3.0-rc.0 version.

@alehro
Copy link

alehro commented Sep 9, 2014

@rbygrave Thank you, your workaround works for me with delay = 500 ms. 100 ms doesn't help.

@objectiveP
Copy link

I encountered this issue in the context of manually bootstrapping my app, as I forgot to remove hg-app directive, which basically leads to location watch infinite digest breakdown. So I can case you are encountering this problem under uncertain circumstances better double check that first.

@mikehayesuk
Copy link

I am running into this problem when doing my own routing. ngRoute has been removed from the app.

I run something like history.pushState(null, null, '/some/page'); to set the current page state and then when the digest runs it hits the infinite error:

10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: function (){var a=d.url(),b=g.$$replace;r&&a==g.absUrl()||(r++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",g.absUrl(),a).defaultPrevented?g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));\ng.$$replace=!1;return r}; newVal: 42; oldVal: 41"],["fn: function (){var a=d.url(),b=g.$$replace;r&&a==g.absUrl()||(r++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",g.absUrl(),a).defaultPrevented?g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));\ng.$$replace=!1;return r}; newVal: 43; oldVal: 42"],["fn: function (){var a=d.url(),b=g.$$replace;r&&a==g.absUrl()||(r++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",g.absUrl(),a).defaultPrevented?g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));\ng.$$replace=!1;return r}; newVal: 44; oldVal: 43"],["fn: function (){var a=d.url(),b=g.$$replace;r&&a==g.absUrl()||(r++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",g.absUrl(),a).defaultPrevented?g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));\ng.$$replace=!1;return r}; newVal: 45; oldVal: 44"],["fn: function (){var a=d.url(),b=g.$$replace;r&&a==g.absUrl()||(r++,c.$evalAsync(function(){c.$broadcast(\"$locationChangeStart\",g.absUrl(),a).defaultPrevented?g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));\ng.$$replace=!1;return r}; newVal: 46; oldVal: 45"]]

I could do with some way to stop Angular from listening to anchor clicks too, which I guess is related to this problem as it's listening for location changes so that it can fire $locationChangeStart etc.

@richardm
Copy link

richardm commented Oct 7, 2014

Also having this same issue using Angular UI Router...

@sgarbesi
Copy link

sgarbesi commented Oct 7, 2014

@BlueHayes @richardm did you try what I mentioned? If you attempt to change the url/history without $location angular goes haywire.

@toblender
Copy link

What fixed the infinite redirecting issue for us was removing the "otherwise" block, from our routeProvider. We have a unique situation of 2 angularJS applications running in the same page, so this fix might not be the fix for everyone.

@camden-kid
Copy link

This problem appeared with migration to 1.3 and was related to injecting $location into a service. It was happily working before the change. Used the suggestion by @swlasse (cheers) to get around the problem.

@elemoine
Copy link

elemoine commented Feb 4, 2015

dca2317, which closed that issue, does not fix the problem raised by @IgorMinar in this issue's initial description. @IgorMinar, the issue you described in #1417 (using "Angular in an existing project that makes use of html5 history (goog.history.Html5History for example) is not IE-specific at all.

This is a major issue. This makes Angular in my way, even when I don't use the $location service.

@elemoine
Copy link

elemoine commented Feb 5, 2015

The only workaround I've found involves monkey-patching the $location provider's $get function. By changing the $get function we prevent the provider from installing the problematic watches. See camptocamp/ngeo#160. I'd really like to fix Angular instead, but, at this point, I don't know what a proper fix would look like.

@smirnovigor
Copy link

In my app I don't use $location and I patch this issue with this decoration:

 $provide.decorator('$location', ['$delegate', '$browser', function($delegate, $browser){
      $delegate.absUrl = function(){return $browser.url();};
      return $delegate;
 }]);

@azachar
Copy link

azachar commented May 5, 2015

Thank you, @smirnovigor ! Well, it works and it doesn't work too. With UI-Router it doesn't replace the url with a new state's url...

@azachar
Copy link

azachar commented May 5, 2015

Hmm, I did simple think and it seems me to that it really helped. I do some redirection after a user logged in and simply wrapping my redirect call into setTimeout method help with the $location event loop.

setTimeout(function({
$location.path('....')
},500);

Not elegant, but it works!

@caviles
Copy link

caviles commented May 13, 2015

You guys are so awesome changing window.replace to $location.path(url_step2_view); worked amazingly. THANKSSSSSSS!!!!!!!!!!

fredrikengstrom added a commit to sklintyg/statistik that referenced this issue Aug 17, 2015
…ortsatte att försöka öppna nya rapporter efter att användaren blivit utloggad. Lösning enligt diskussion här: angular/angular.js#1417
@M0ns1gn0r
Copy link

Thanks @azachar! Your timeout workaround helped in my case.

@creativegeekjp
Copy link

@azachar's timeout workaround worked for me too.

@comfroels
Copy link

@smirnovigor You are the MAN! Been working on this for 2 days! The decorator works everyone!

@leye0
Copy link

leye0 commented Nov 10, 2015

@caviles You didn't solve a bug, you weren't using $location. This bug is about $location.

@natee
Copy link

natee commented May 3, 2016

In my case, I used window.location.href = 'xxx', when I changed it to $location.path(), It worked fine.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests