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

Internet Explorer infinite digest with location change #15556

Closed
thetrevdev opened this issue Dec 29, 2016 · 5 comments
Closed

Internet Explorer infinite digest with location change #15556

thetrevdev opened this issue Dec 29, 2016 · 5 comments

Comments

@thetrevdev
Copy link

Bug

Current behavior

http://plnkr.co/edit/7SldRRsrYFO32hiG9abV?p=preview
Infinite digest error in Internet Explorer console after button click
[$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []

Expected behavior

Location changes without error

Minimal reproduction of the problem with instructions
http://plnkr.co/edit/7SldRRsrYFO32hiG9abV?p=preview with Internet Explorer
Check error console after button click.
Note: this happens when the location change occurs during an $evalAsync

What is the motivation / use case for changing the behavior?
We have a large app with different redirect scenarios that may or may not be local to our app. We wish to set location.href.

  • Angular version: 1.5/1.6

  • Browser: [ IE 11 ]

In internet explorer the hashchange event fires asynchronously. this causes the $locationWatch to think that the $location url was updated as it doesn't match $browser url. It seems like the location logic should know whether a url change occured via $location setters

**Relevant Code:
https://github.com/angular/angular.js/blob/master/src/ng/location.js#L970

@thetrevdev thetrevdev changed the title Internet Explorer infinite digest with location change during Internet Explorer infinite digest with location change Dec 29, 2016
@gkalpak
Copy link
Member

gkalpak commented Dec 30, 2016

In internet explorer the hashchange event fires asynchronously.

Actually, this has nothing to do with the hashchange event being fired asynchronously (the same happens in Chrome as well). The problem is that in IE11 there is no popstate event fired when only the hash changes, which is fired (synchronously) in other browsers. This is a known bug in IE11 and there is no intention to fix it 😞

@Narretz
Copy link
Contributor

Narretz commented Dec 30, 2016

@thetrevdev Do you have a workaround for this? It looks like it's difficult to fix at this point.

gkalpak added a commit to gkalpak/angular.js that referenced this issue Dec 30, 2016
gkalpak added a commit to gkalpak/angular.js that referenced this issue Dec 30, 2016
@gkalpak
Copy link
Member

gkalpak commented Dec 30, 2016

It seems like the location logic should know whether a url change occured via $location setters

This sounds reasonable. Based on that, I have a potential fix here. (I still need to add tests, but it didn't break any of the existing tests, which is a good sign 😃)
Here is the updated plnkr with the fix.

@gkalpak gkalpak added this to the Backlog milestone Dec 30, 2016
gkalpak added a commit to gkalpak/angular.js that referenced this issue Dec 30, 2016
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This wasn't an issue in most browsers, where the `popstate` event was fired
synchronously. In IE11 though, the `popstate` event is not fired at all for
hash-only changes ([known bug][1]) and the `hashchange` event is fired
asynchronously (which is too late).

This commit fixes it by keeping track of `$location` setter methods being called
and only processing a URL change if it originated from such a call. If there is
a URL difference but no setter method has been called, this means that the
browser URL has been updated directly and the change hasn't yet been propagated
to `$location` (e.g. due to no synchronous `popstate` event). In that case, the
change will be propagated subsequently by the (asynchronous) `hashchange` event.

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes angular#15556
gkalpak added a commit to gkalpak/angular.js that referenced this issue Dec 30, 2016
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This wasn't an issue in most browsers, where the `popstate` event was fired
synchronously. In IE11 though, the `popstate` event is not fired at all for
hash-only changes ([known bug][1]) and the `hashchange` event is fired
asynchronously (which is too late).

This commit fixes it by keeping track of `$location` setter methods being called
and only processing a URL change if it originated from such a call. If there is
a URL difference but no setter method has been called, this means that the
browser URL has been updated directly and the change hasn't yet been propagated
to `$location` (e.g. due to no synchronous `popstate` event). In that case, the
change will be propagated later by the (asynchronous) `hashchange` event.

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes angular#15556
@gkalpak
Copy link
Member

gkalpak commented Dec 30, 2016

There you go: #15561

@thetrevdev
Copy link
Author

Tested it in our app. Looks good in our tests.

Thanks!

gkalpak added a commit to gkalpak/angular.js that referenced this issue Jan 4, 2017
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue in
IE11, where (unlike other browsers) no `popstate` event is fired at all for
hash-only changes ([known bug][1]) and the `hashchange` event is fired
asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes angular#11075
Fixes angular#12571
Fixes angular#15556
gkalpak added a commit to gkalpak/angular.js that referenced this issue Jan 4, 2017
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue when
setting `location.href` in IE11, where (unlike other browsers) no `popstate`
event is fired at all for hash-only changes ([known bug][1]) and the
`hashchange` event is fired asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes angular#11075
Fixes angular#12571
Fixes angular#15556
@gkalpak gkalpak closed this as completed in 752f411 Jan 9, 2017
gkalpak added a commit that referenced this issue Jan 9, 2017
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue when
setting `location.href` in IE11, where (unlike other browsers) no `popstate`
event is fired at all for hash-only changes ([known bug][1]) and the
`hashchange` event is fired asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes #11075
Fixes #12571
Fixes #15556

Closes #15561
ellimist pushed a commit to ellimist/angular.js that referenced this issue Mar 15, 2017
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue when
setting `location.href` in IE11, where (unlike other browsers) no `popstate`
event is fired at all for hash-only changes ([known bug][1]) and the
`hashchange` event is fired asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes angular#11075
Fixes angular#12571
Fixes angular#15556

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

No branches or pull requests

3 participants