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

Infinite digest loop occurring on first run of app when running grunt server #10423

Closed
jaredwilli opened this issue Dec 11, 2014 · 16 comments
Closed

Comments

@jaredwilli
Copy link

I tried to update my app to use 1.3.6 but when I do for some reason an infinite digest loop happens when I run grunt server and the app opens in the browser. It appears to redirect in the url bar over and over before it shows anything until finally it shows the page. Looking at the console I can see there are several errors for this.

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.3.6/$rootScope/infdig?p0=10&p1=%5B%5D
    at http://localhost:9003/bower_components/angular/angular.js:63:12
    at Scope.$digest (http://localhost:9003/bower_components/angular/angular.js:14233:19)
    at Scope.$apply (http://localhost:9003/bower_components/angular/angular.js:14457:24)
    at bootstrapApply (http://localhost:9003/bower_components/angular/angular.js:1441:15)
    at Object.invoke (http://localhost:9003/bower_components/angular/angular.js:4169:17)
    at doBootstrap (http://localhost:9003/bower_components/angular/angular.js:1439:14)
    at bootstrap (http://localhost:9003/bower_components/angular/angular.js:1459:12)
    at angularInit (http://localhost:9003/bower_components/angular/angular.js:1353:5)
    at HTMLDocument.<anonymous> (http://localhost:9003/bower_components/angular/angular.js:25995:5)
    at fire (http://localhost:9003/bower_components/jquery/dist/jquery.js:3073:30)

When I refresh the page though, it doesn't happen. Only on the first run of the app.

The last version of angular that it doesn't do this in is 1.3.3.

@jaredwilli
Copy link
Author

Could be related to this issue angular-ui/ui-router#1616

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

ping @petebacondarwin

@caitp
Copy link
Contributor

caitp commented Dec 11, 2014

tentatively moving this to 1.3.8 but I don't expect it will be fixed immediately --- I think this is a duplicate issue too, lemme find the other

@petebacondarwin
Copy link
Contributor

@jared will can you create a reproduction to demonstrate this?

@jaredwilli
Copy link
Author

@petebacondarwin I have been trying to but unsuccessfully so far.

@petebacondarwin
Copy link
Contributor

I suspect it is related to the $location rewriting of the path. What is you $location config? Are you using HTML5Mode?

@m3kka
Copy link

m3kka commented Dec 12, 2014

I think that my issue is somehow related #10405

The problem seems to involve the way $location deals with hashbang URLs.

@jaredwilli
Copy link
Author

I am not using HTML5Mode no.

@therajumandapati
Copy link

@jaredwilli Can you share the code somehow ? If you can't do Plnkr, how about bundling your code and shipping it to me ?

Because I had the same problem some time ago, but unfortunately I lost the code. I'll try to reproduce this.

@Narretz
Copy link
Contributor

Narretz commented Apr 20, 2015

I have put this into the Icebox since there is no reproduction and there is no one else experiencing this problem.

@gkalpak
Copy link
Member

gkalpak commented May 29, 2015

I think I was able to reproduce it (it's a much broader usecase - nothing to do with grunt server).

Basically, (I think) it happens when you have an empty $location#path and try to set a $location#hash during the initial bootstrap apply (e.g. in a run block, or during the instantiation of a controller).

Live reproduction

This is happening since v1.3.6 (and up to at least v1.4.0) and the problem is (afaict) in ng/browser.js on this line:

location.hash = getHash(url);

This is what happens:

  1. Initial state:
    • location.href === 'http://example.com/index.html'
    • $location.path() === ''
    • $location.hash() === ''
  2. Action:
    • $location.hash('foo')
  3. Results into:
    • $location.url() === '##foo' <--- inserts empty path
    • $location.absUrl() === 'http://example.com/index.html##test'
  4. In turn results into:
    • location.hash = getHash('http://example.com/index.html##test') (see ng/browser.js)

Here is where the issue starts:

getHash() looks for the first # (if any) and returns the substring of the URL starting right after the first # (in this case, it will return #test). So, by calling location.hash = '#test' we really want to have .../index.html##test (notice the double #).
Unfortuntely, (at least on Chrome/FF/IE11 that I tried) location.hash tries to be smart and handle values both with and without the leading #. I.e. location.hash = 'foo' is equivalent to location.hash = '#foo' and both will result into .../index.html#foo. (Essentially, in the former case it adds the # automatically since it was missing, but in the latter case it doesn't add a #, because it thinks it's already there.)

(Assuming it works the same way in the rest of the supported browsers), the fix should be simple:

Just keep the leading # when setting location.hash. I.e. change getHash()'s implementation like this:

function getHash(url) {
  var index = url.indexOf('#');
  //return index === -1 ? '' : url.substr(index + 1);
  return index === -1 ? '' : url.substr(index);
}

/cc @petebacondarwin (since you are currently working on $location issues)

@petebacondarwin petebacondarwin self-assigned this May 29, 2015
@petebacondarwin petebacondarwin modified the milestones: 1.4.1, Ice Box May 29, 2015
@petebacondarwin
Copy link
Contributor

@jaredwilli and @gkalpak - can you see if 91b6022 fixed this?

@petebacondarwin
Copy link
Contributor

I see that it doesn't on the live reproduction.

@jaredwilli
Copy link
Author

I no longer have access to the code base that I was seeing this issue with. I have a different job now so can't test if this works.

@petebacondarwin petebacondarwin modified the milestones: 1.4.1, 1.4.2 Jun 16, 2015
petebacondarwin added a commit to petebacondarwin/angular.js that referenced this issue Jun 17, 2015
… no hashPrefix

The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes angular#10423
@petebacondarwin
Copy link
Contributor

I have a fix for this, using @gkalpak's suggestion. Please can you review?

@gkalpak
Copy link
Member

gkalpak commented Jun 17, 2015

#12145 seems to fix my live reproduction scenario.

petebacondarwin added a commit to petebacondarwin/angular.js that referenced this issue Jun 17, 2015
… no hashPrefix

The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes angular#10423
Closes angular#12145
petebacondarwin added a commit that referenced this issue Jun 17, 2015
… no hashPrefix

The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes #10423
Closes #12145
netman92 pushed a commit to netman92/angular.js that referenced this issue Aug 8, 2015
… no hashPrefix

The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes angular#10423
Closes angular#12145
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

10 participants