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

infinite $digest when using history api #6976

Closed
mrhooray opened this issue Apr 3, 2014 · 9 comments
Closed

infinite $digest when using history api #6976

mrhooray opened this issue Apr 3, 2014 · 9 comments

Comments

@mrhooray
Copy link

mrhooray commented Apr 3, 2014

I encountered this issue when using Dropbox Datastore API, which invokes history.replaceState after OAuth redirection. By adding a callback to $routeChangeStart event, it seems the app is doing routing repeatedly.

Only use $location may not always be possible when involving third party library or other app. Is there a workaround or imminent fix on this?

Angular Version(s): 1.2.15
Browsers and Operating System: Chrome 33 on OS X 10.9
Reproduce Error: http://jsfiddle.net/7t7eK/ from #1417 with Angular 1.2.15
Related issues: #1417 , #3924

Error and trace that I got:

10x on $routeChangeStart
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: $locationWatch; newVal: 7; oldVal: 6"],["fn: $locationWatch; newVal: 8; oldVal: 7"],["fn: $locationWatch; newVal: 9; oldVal: 8"],["fn: $locationWatch; newVal: 10; oldVal: 9"],["fn: $locationWatch; newVal: 11; oldVal: 10"]]
http://errors.angularjs.org/1.2.15/$rootScope/infdig?p0=10&p1=%5B%5B%22fn%3…2fn%3A%20%24locationWatch%3B%20newVal%3A%2011%3B%20oldVal%3A%2010%22%5D%5D
    at http://localhost:9000/bower_components/angular/angular.js:78:12
    at Scope.$digest (http://localhost:9000/bower_components/angular/angular.js:12070:19)
    at Scope.$apply (http://localhost:9000/bower_components/angular/angular.js:12279:24)
    at http://localhost:9000/bower_components/angular/angular.js:1382:15
    at Object.invoke (http://localhost:9000/bower_components/angular/angular.js:3805:17)
    at doBootstrap (http://localhost:9000/bower_components/angular/angular.js:1380:14)
    at bootstrap (http://localhost:9000/bower_components/angular/angular.js:1394:12)
    at angularInit (http://localhost:9000/bower_components/angular/angular.js:1307:5)
    at http://localhost:9000/bower_components/angular/angular.js:21163:5
    at HTMLDocument.trigger (http://localhost:9000/bower_components/angular/angular.js:2445:7) angular.js:9563
(anonymous function) angular.js:9563
(anonymous function) angular.js:7004
Scope.$apply angular.js:12281
(anonymous function) angular.js:1382
invoke angular.js:3805
doBootstrap angular.js:1380
bootstrap angular.js:1394
angularInit angular.js:1307
(anonymous function) angular.js:21163
trigger angular.js:2445
(anonymous function) angular.js:2716
forEach angular.js:330
eventHandler angular.js:2715

Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: $locationWatch; newVal: 7; oldVal: 6"],["fn: $locationWatch; newVal: 8; oldVal: 7"],["fn: $locationWatch; newVal: 9; oldV...<omitted>...5D angular.js:78
(anonymous function) angular.js:78
Scope.$digest angular.js:12070
Scope.$apply angular.js:12279
(anonymous function) angular.js:1382
invoke angular.js:3805
doBootstrap angular.js:1380
bootstrap angular.js:1394
angularInit angular.js:1307
(anonymous function) angular.js:21163
trigger angular.js:2445
(anonymous function) angular.js:2716
forEach angular.js:330
eventHandler angular.js:2715
on $routeChangeStart 
on $routeChangeStart 
on $routeChangeStart 
@m3kka
Copy link

m3kka commented Apr 10, 2014

This problem present itself also on Windows Phone running IE9.

@IgorMinar
Copy link
Contributor

@jeffbcross this looks similar to the encoding issue we discussed today (#5019)

@IgorMinar
Copy link
Contributor

@jeffbcross can you please take a look

@IgorMinar IgorMinar added this to the 1.3.0 milestone Jul 25, 2014
@jeffbcross
Copy link
Contributor

@IgorMinar yep

@jeffbcross jeffbcross modified the milestones: 1.3.0, 1.3.0-beta.18 Jul 28, 2014
@jeffbcross
Copy link
Contributor

Reproduction as test:

  it('should not cause infinite digest when replacing outside of Angular', function() {
      module(function($windowProvider, $locationProvider, $browserProvider) {
        // $locationProvider.html5Mode(true);

        $browserProvider.$get = function($document, $window) {
          var sniffer = {history: true, hashchange: true}
          var logs = {log:[], warn:[], info:[], error:[]};
          var fakeLog = {log: function() { logs.log.push(slice.call(arguments)); },
                     warn: function() { logs.warn.push(slice.call(arguments)); },
                     info: function() { logs.info.push(slice.call(arguments)); },
                     error: function() { logs.error.push(slice.call(arguments)); }};


          var b = new Browser($window, $document, fakeLog, sniffer);
          b.pollFns = [];
          return b;
        }
      });

      inject(function($rootScope, $location){
        window.history.replaceState(null, '', '/hello');
        expect(function() {
          $rootScope.$digest();
        }).not.toThrow();
      });
    });

@jeffbcross
Copy link
Contributor

The problem is caused because Angular isn't synchronously notified of changes to the browser's url, and trusts its cached version of the url inside $browser.url(). Since $browser.url() as a getter is idempotent, it keeps returning the true url without updating its internal record of what the url is.

To verify that this is true, add this line in the provided test just after the window.history.replaceState line, and the test will pass: angular.element($window).triggerHandler('popstate');

This issue could be easily fixed with one line inside of $browser, by resetting lastBrowserUrl inside the url getter: lastBrowserUrl = newLocation || location.href.replace(/%27/g,"'"); But this could cause unwanted side effects. Still exploring...

@jeffbcross
Copy link
Contributor

My fix from the previous comment fixes the infinite digest error, but doesn't actually retain the new path.

jeffbcross added a commit to jeffbcross/angular.js that referenced this issue Jul 29, 2014
jeffbcross added a commit to jeffbcross/angular.js that referenced this issue Jul 30, 2014
…de of Angular

$browser keeps an internal representation of the browser's url, and although $browser.url()
would return the client's real current url, the $location service that was comparing that
url with $location's representation had no way of knowing when the url had been changed outside
of Angular.

This commit makes browser and location a little bit smarter by setting a flag inside of $browser
when it detects that a url changed outside of Angular, allowing $location to parse the new url
and update its own internal representation of the url (and react appropriately).

Fixes angular#6976
jeffbcross added a commit to jeffbcross/angular.js that referenced this issue Jul 30, 2014
…de of Angular

$browser keeps an internal representation of the browser's url, and although $browser.url()
would return the client's real current url, the $location service that was comparing that
url with $location's representation had no way of knowing when the url had been changed outside
of Angular.

This commit makes browser and location a little bit smarter by setting a flag inside of $browser
when it detects that a url changed outside of Angular, allowing $location to parse the new url
and update its own internal representation of the url (and react appropriately).

Fixes angular#6976
@caitp caitp modified the milestones: 1.3.0-beta.18, 1.3.0-beta.19 Aug 8, 2014
@btford btford removed the gh: issue label Aug 20, 2014
@btford btford modified the milestones: 1.3.0-beta.19, 1.3.0-beta.20 Aug 22, 2014
jeffbcross added a commit to jeffbcross/angular.js that referenced this issue Aug 26, 2014
…de of Angular

$browser keeps an internal representation of the browser's url, and although $browser.url()
would return the client's real current url, the $location service that was comparing that
url with $location's representation had no way of knowing when the url had been changed outside
of Angular.

This commit makes browser and location a little bit smarter by setting a flag inside of $browser
when it detects that a url changed outside of Angular, allowing $location to parse the new url
and update its own internal representation of the url (and react appropriately).

Fixes angular#6976
tbosch added a commit to tbosch/angular.js that referenced this issue Aug 26, 2014
tbosch added a commit to tbosch/angular.js that referenced this issue Aug 26, 2014
tbosch added a commit to tbosch/angular.js that referenced this issue Aug 27, 2014
tbosch added a commit to tbosch/angular.js that referenced this issue Aug 27, 2014
@tbosch tbosch closed this as completed in 3be00df Aug 27, 2014
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes after a reload happens

Removes unnecessary caching from $browser, as IE-IE9 all allow to change `location.href` synchronously, i.e. after changing it (via property write or `location.replace) it can be read out immediately with the up to date value.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all 
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all 
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
tbosch added a commit to tbosch/angular.js that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
Closes angular#9455
tbosch added a commit that referenced this issue Oct 7, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for #6976
Fixes #9235
Closes #9470
bullgare pushed a commit to bullgare/angular.js that referenced this issue Oct 9, 2014
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca2317 and d707114.

Adds more tests for angular#6976
Fixes angular#9235
Closes angular#9455
@naureen-34
Copy link

Was this issue resolved? We still get this issue on using "history.replaceState"

@gkalpak
Copy link
Member

gkalpak commented May 7, 2016

This is supposed to be resolved for quite a while. If you are still having issues, please open a new bug report (providing the necessary info).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.