Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite $digest Loop if we do preventDefault() in $stateChangeStart event #600

Closed
tamtakoe opened this issue Nov 25, 2013 · 76 comments
Closed

Comments

@tamtakoe
Copy link

Why $stateChangeStart event fire many times?
http://plnkr.co/edit/QYvRXxciPL9fnyuxiF3l?p=preview

@timkindberg
Copy link
Contributor

Link isn't working "Import Error - Plunk not found!"

@laurelnaiad
Copy link

You need to put $stateProvider before your .state calls.

Also, this comment makes an untrue assumption:

          //Warning! If we go to incorrect link (e.g. /msk/news/smth)
          //we peridically do this code until we get city.
          //Some times we get error Infinite $digest Loop

In fact, /msk/news/smth is not a URL pattern in your state definitions, so it is never going to get there. You might want to redo your plunk.

@tamtakoe
Copy link
Author

Link isn't working "Import Error - Plunk not found!"
Try fiddle http://jsfiddle.net/tamtakoe/5AgSX/. Copy code to local machine

I can not defines all incorrect links in states, that's why I have $urlRouterProvider.otherwise('/'). In fact I must to go to start state (url: '/') one time, but I do event.preventDefault() and ui-router try to go to '/' through and through.

Now I don't prevent incorrect change

if (toState.url !== '/') {
    event.preventDefault();
}

It works.

Can I see $urlRouterProvider.otherwise url into $stateChangeStart handler? For this

if (toState.url !== $urlRouterProvider.otherwise.url) {
    event.preventDefault();
}

@dmaevsky
Copy link

Bumped into the same bug yesterday. If you get into the $startStateChange handler from $urlRouterProvider.otherwise URL and call preventDefault(), you end up in an infinite loop. The only solution for now was to create a bogus 'startup' state and never prevent going there, and just add $state.go('login') as the only line in the 'startup' state controller, where 'login' is what I actually wanted to put into $urlRouterProvider.otherwise.

@stevenvegt
Copy link

I have probably the same problem here.
Let me explain my flow:
If the user enters the page on '/', or whatever unknown url, he should go to his profile '/person/me'. For this I use the otherwise method.
To ensure a user is logged in, I listen on stateChangeStart and call preventDefault when the requested state is protected && !user.authenticated and do a stage.transitionTo 'login'. But then somehow i see a change back to the otherwise url '/person/me' and it starts all over again until the recurrence protection kicks in.

If the user enters a known url, the otherwise path is not used, the user sees the login page and all is good. So I think it has to do with the otherwise method, and probably combined with the preventDefault call.

@frankwallis
Copy link

I also had this issue, you can fix it by using the other version of otherwise which takes a function:

    $urlRouterProvider.otherwise( function($injector, $location) {
            var $state = $injector.get("$state");
            $state.go("app.home");
        });

@aymericbeaumet
Copy link

Was encountering a similar issue. The otherwise's function version did the trick. Thanks @frankwallis 👍

@BANG88
Copy link

BANG88 commented Dec 16, 2014

@frankwallis 👍

@stryju
Copy link

stryju commented Jan 14, 2015

@frankwallis thanks! this workaround seems to fix it (no infinite loop YET 😉)

@cgiacomi
Copy link

I have the same issue.

Angular 1.3.9
ui-router 0.2.13

I have also tried by adding the following

$urlRouterProvider.otherwise( function($injector, $location) {
    var $state = $injector.get("$state");
        $state.go("app.home");
});

but no luck.

I have added the following as well to debug:

$rootScope.$on('$stateChangeError',function(event, toState, toParams, fromState, fromParams){
  console.log('$stateChangeError - fired when an error occurs during transition.');
  console.log(arguments);
});

$rootScope.$on('$stateNotFound',function(event, unfoundState, fromState, fromParams){
  console.log('$stateNotFound '+unfoundState.name +'  - fired when a state cannot be found by its name.');
  console.log(unfoundState, fromState, fromParams);
});

but they never get called and I still have the infinite loop problem.

It seems to be a problem with present/missing / slashes at the end of the url.

I have downgraded to angular 1.3.0 and the problem goes away, and everything works as it's supposed to.

@aymericbeaumet
Copy link

@cgiacomi I recently updated to Angular 1.3.8 / Angular-ui-router 0.2.13 and I do not face your issue. Can you upgrade to those versions to see if the problem comes from 1.3.9?

@cgiacomi
Copy link

Hi I have just tested with angular 1.3.8 as well and I still have the problem.

I have though found that the issue is somehow linked to a redirect to /#/ and that seems to fall into the otherwise callback.

I'll try to explain the setup of my routes:

routes.js

$urlRouterProvider.when('', '/home');

$urlRouterProvider.otherwise( function($injector, $location) {
    var $state = $injector.get("$state");
    $state.go("notfound"); //redirect to a 404 page
});

... //rest of the routes

config.js

... some config code
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
  logger.info('stateChangeStart');

  if(!IdentityService.isAuthenticated() && toState.name !== 'login') {
    //user is not logged in and is attempting to go to a different page than the login page.
    $sessionStorage.toState = toState; //save for redirect after successful login
    $sessionStorage.toParams = toParams; //save for redirect after successful login

    logger.info('redirecting to login page.');
    $state.go("login");
    event.preventDefault();
  }

What I have noticed is that if, lets say I navigare to localhost:8000

  1. ui router redirects me to /home
  2. the check for authenticated user occurs
  3. I am ofc redirected to /login
  4. the otherwise callback is executed with a the following parameters values for $location:
$$absUrl: "http://localhost:8000/#/",
$$path: '/',
$$url: '/'

It's like somehow I am redirected to http://localhost:8000/#/ and of course I don't have a route for / which causes the otherwise route to be invoked. Then ofc I end up in the infinite loop. If I remove the otherwise route things work fine. But again with Angular 1.3.0 everything worked fine.

Hope that helps, and thanks for the help :)

One final thing, if I navigare directly to http://localhost:8000/#/home instead of just http://localhost:8000 then everything works fine.

@mark-veenstra
Copy link

Solution of @frankwallis works perfectly on 1.3.6

@vlapo
Copy link

vlapo commented Apr 23, 2015

Thnx @frankwallis. Works on ancular 1.3.15, ui-router 0.2.13.

@rainboxx
Copy link

rainboxx commented May 8, 2015

We're having this issue, too. We don't have a state for '/' and redirect to a initial state, which will end in an infinite digest loop. Using the function call fixes this issue but is quite an ugly hack.

@rainboxx
Copy link

rainboxx commented May 8, 2015

So what ui-router does within otherwise is to change the location itself:

if (isString(handled)) $location.replace().url(handled);
. And it seems that changing the state in the aftermath is causing an issue here while, with the function example above, no location change will be triggered initially but a state change.

@sars
Copy link

sars commented May 9, 2015

I tried to debug this issue and got the following:

  1. Open url, that has no state configured
  2. It is handles by otherwise and changes $location to "otherwise" here: https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L2011
  3. Angular watches this changes (loop https://github.com/angular/code.angularjs.org/blob/master/1.3.9/angular.js#L14208)
    It handles this changes by https://github.com/angular/code.angularjs.org/blob/master/1.3.9/angular.js#L11398
    And sync browser url https://github.com/angular/code.angularjs.org/blob/master/1.3.9/angular.js#L11422
    Also it broadcasts $locationChangeSuccess.
  4. Ui-router listens $locationChangeSuccess with update function https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L2001
    It handles new url by existing state and runs transitionTo() eventually: https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L2347
  5. transitionTo broadcast $stateChangeStart https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L3225
  6. We listen $stateChangeStart and do preventDefault(). It causes $urlRouter.update() without arguments https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L3227
    and change location back: https://github.com/angular-ui/ui-router/blob/master/release/angular-ui-router.js#L2072
  7. Angular sees than $location not match browser url (browser url now "otherwise"):
    https://github.com/angular/code.angularjs.org/blob/master/1.3.9/angular.js#L11405
    It changes browser url to not existent and broadcasts $locationChangeSuccess
  8. Ui-router handle it and change location again to "otherwise"
  9. Angular sees that $location and browser url not match and so on

@sars
Copy link

sars commented May 9, 2015

The problem in logic:
We open not existent url (state)
We change state to otherwise
BUT we deny this change by preventDefault()

@welkinwong
Copy link

SO EASY. Thanks @frankwallis

@tomchiverton
Copy link

There's something similar wrong with

$urlRouterProvider.when('/home','/home/global');

because it triggers the same iteration counts and similar work around ( a function as second argument in this case).

@matteo-mosca
Copy link

@frankwallis solution worked for me too, thanks. It is, anyway, a workaround around a released feature that does not work as intended. Hope it will be fixed soon.

@samueltbrown
Copy link

+1 for @frankwallis workaround. Bailed me out today!

@axedre
Copy link

axedre commented Jun 10, 2015

+1 @frankwallis you made my day! Fixes issue in angular-ui 0.2.13 + angularjs 1.3.13.

aql added a commit to drnear/dr.near_app that referenced this issue Jun 13, 2015
@youyfeng
Copy link

Thinks @frankwallis. Fixes issue in angular-ui-router 0.2.13 + angular1.3.15

@btamayo
Copy link

btamayo commented Nov 23, 2015

@frankwallis 👍 Thank you!

@umbacclifford
Copy link

@frankwallis .Thank you!

@hnguyenec
Copy link

@frankwallis +1

@dellheng
Copy link

dellheng commented Dec 1, 2015

@frankwallis +1

@shmuelgutman
Copy link

@frankwallis Thank you!!

@Elmanderrr
Copy link

@frankwallis +1

1 similar comment
@tania-pets
Copy link

@frankwallis +1

@venkateshappala
Copy link

I have HTML with Login and Logout buttons. On successful login, I am also facing infinite loop issue. Please find attached code. Simple code. Can anyone help me on applying workaround for this issue?
Thanks in advance!!
Adal-Angular.zip

@brendanluna
Copy link

Amazing that this still isn't fixed.

@frankwallis another +1 to you.

@nateabele
Copy link
Contributor

@Blunatic Well, it's a large, widely-used project, so we have a lot to focus on. This isn't high on our priority list because, at first glance, the fix is non-trivial, and what's more, there's a simple, unobtrusive workaround.

If it's so 'amazing' to you that we have yet to take time out of our busy schedules to fix it anyway, maybe you could take some time out of yours and start here. 😄

@brendanluna
Copy link

@nateabele understandable. Didn't mean to come across as a jerk. My apologies.

@agupta1989
Copy link

@nateabele 👍

@nateabele
Copy link
Contributor

Not at all. Just explaining the rationale.

@christopherthielen
Copy link
Contributor

I came to the same conclusion as @sars comment. See #2238
Fix should be forthcoming in 0.2.16

christopherthielen added a commit that referenced this issue Jan 23, 2016
@pjwalmsley
Copy link

@venkateshappala

Hope you figured things out. I really don't think that's enough context, I'd presume the issue lies somewhere in the adal.login method.

@jbennie
Copy link

jbennie commented Jan 26, 2016

// the following code will allow you to default to the login , unless your are logged in then it will default to the preferred page.

Make sure that the

$stateProvider.state .... ; //this must be first otherwise the state object you want to go to wont exist.

$urlRouterProvider.otherwise(function ($injector, $location) {
var $state = $injector.get("$state");
var authService = $injector.get("authService");

        if (authService.authentication.isAuth) {
            $state.go("library.search");
        } else {
            $state.go("home.login");
        }
    });

// then in run

$rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
// if ($rootScope.appAuth.authentication.isAuth == false && $rootScope.appAuth.authentication.profile !== 'undefined')

        if (authService) {
            if (!authService.IsAnnonPage(toState.name)) {
                if (!authService.authentication.isAuth && toState.name != 'home.login') {
                    event.preventDefault();

                    $state.go('home.login');
                    return;
                }
            }
        }
        else {
            $state.go('home.login');
        }

    });

@ggarg2
Copy link

ggarg2 commented Feb 1, 2016

@frankwallis +1

@masoodtrb
Copy link

@frankwallis Thank you

@sandor11
Copy link

sandor11 commented Mar 7, 2016

@frankwallis Thanks! You have just ended 2 hours of pain.

@nfantone
Copy link

This is still an on going issue. @frankwallis workaround is still the way to go.

#1022 (comment)

Wasn't fixed on 0.2.16 as @christopherthielen mentioned.

This shouldn't be closed. (cc. @nateabele)

@nigel-dewar
Copy link

Wow is this issue really not fixed yet??

@nfantone
Copy link

nfantone commented Apr 2, 2016

Noup, it's not.

@nigel-dewar
Copy link

@nfantone , thanks for letting me know. cheers.

@andreiho
Copy link

andreiho commented Aug 4, 2016

Almost 3 years later, still experiencing the same issue. Thankfully @frankwallis workaround does the trick.

For anybody coming across this issue, here's the workaround (so you don't have to look for it in this long thread):

I also had this issue, you can fix it by using the other version of otherwise which takes a function:

$urlRouterProvider.otherwise(function($injector) {
  var $state = $injector.get('$state');
  $state.go('app.home');
});

@angular-ui angular-ui locked and limited conversation to collaborators Aug 4, 2016
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