Skip to content

Browser navigation broken #2192

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

Closed
fgarit opened this issue Aug 20, 2015 · 10 comments
Closed

Browser navigation broken #2192

fgarit opened this issue Aug 20, 2015 · 10 comments

Comments

@fgarit
Copy link

fgarit commented Aug 20, 2015

Hi, I first mentioned this problem in #1699 (comment)

I have a problem with the browser back button looping.

The following does not break the history API:

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    if (notLoggedIn) {
        event.preventDefault();
        $state.go('login');
    }
});

The following does:

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    event.preventDefault();

    $timeout(function() {
        if (notLoggedIn) {
            $state.go('login');
        } else {
            $state.go(toState, toParams);
        }
    });
});

(obviously, I'm not just setting a timeout for the heck of it, but I'm doing some ajax call)

My fix is the following, in angular-ui-router.js version 0.2.15 line 3231:

if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
  $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
  //$urlRouter.update(); // this line causes the issue in case of browser nav
  return TransitionPrevented;
}

The problematic case with this $urlRouter.update call is the following:
When I hit the browser back button, the url is updated by the browser before $stateChangeStart is called (as opposed to when there is a normal $state.go nav).

Since event.preventDefault was called on the $stateChangeStart event, $urlRouter.update is called. In the code below, $location.url() is not equal to location ($location.url() is the one of the toState and location is the one of the fromState).
So $location.url(location); $location.replace(); are called with the url of the fromState which creates a new browser history entry I think (this explains the loop). I'm using chrome 43.0 btw, but I don't think it's relevant.

angular-ui-router version 0.2.15 line 2064:

update: function(read) {
  if (read) {
    location = $location.url();
    return;
  }
  if ($location.url() === location) return; // toStateUrl !== from fromStateUrl

  $location.url(location); // $location.url(fromStateUrl);
  $location.replace();
},

I suppose there is a good reason for that $urlRouter.update call. Maybe we could set some property on the $stateChangeStart event to let the router know not to make that call.

var event = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);
if (event.defaultPrevented) {
  $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
  if (event.noUpdate !== true) {
    $urlRouter.update();
  }
  return TransitionPrevented;
}
event.preventDefault();
event.noUpdate = true;

It may not look very clean, but I can't think of another way around.

@fgarit fgarit changed the title Browser back broken Browser navigation broken Aug 20, 2015
@eddiemonge
Copy link
Contributor

why are you doing an ajax call in the state change event?

@fgarit
Copy link
Author

fgarit commented Sep 11, 2015

I'm doing an authentication check.
I used to do it in resolves but the results weren't satisfactory.
I know of no better place to do it than in the state change event, in my case.

@ProLoser
Copy link
Member

No idea what you mean by the results weren't satisfactory

@fgarit
Copy link
Author

fgarit commented Sep 11, 2015

I used to have a resolve for every single state of the app so that there could be an ajax request performed before entering any step (for authentication checks).

The problem is that moving back from a child state to its parent state, wouldn't trigger the authentication resolve of the parent state again.

I spent a lot of time on this and I ended up going for the much simpler solution of putting the authentication check in the state transition.

It made more sense to me to have it there too.

And it's working perfectly now btw, with the workaround I proposed.

@fgarit
Copy link
Author

fgarit commented Sep 22, 2015

Could this maybe be reopened?

@nateabele
Copy link
Contributor

@fgarit
Copy link
Author

fgarit commented Sep 22, 2015

@nateabele I'm pretty sure I've tried that but I couldn't get access to toState and toParams from the $locationChangeSuccess

@nateabele
Copy link
Contributor

Right, you'd need some intermediary service to synchronize the two.

@jekrogs
Copy link

jekrogs commented May 11, 2016

@FLO-G Did you find a solution?

@hwangTPN
Copy link

I am using 0.2.18, I added

event.preventDefault();
$state.transition = true;

and it works now

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

No branches or pull requests

6 participants