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

Deactivation Guard breaks the routing history #13586

Open
patrickracicot opened this issue Dec 20, 2016 · 106 comments
Open

Deactivation Guard breaks the routing history #13586

patrickracicot opened this issue Dec 20, 2016 · 106 comments

Comments

@patrickracicot
Copy link

@patrickracicot patrickracicot commented Dec 20, 2016

I'm submitting a ... (check one with "x")

[X ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
Currently, I have a deactivation guard on a route which will return false or true depending on a condition. To get to this guarded route, the user must pass though 3 navigation step. Now, once on the guarded route, when using location.back(), the guard is called. If it returns true, the previous route is loaded. If the guard returns false, the navigation is cancelled. But if we redo a Location.back() after a failed navigation, the previous route that will be loaded will be 2 steps in the history instead of 1 (user perception).

Workflow

Main    -- navigate to     --> Route 1
Route 1 -- navigate to     --> Route 2
Route 2 -- navigate to     --> Route 3
Route 3 -- location.back() --> guard returns true  --> Route 2
Route 2 -- navigate to     --> Route 3
Route 3 -- location.back() --> guard returns false --> Route 3
Route 3 -- location.back() --> guard returns true  --> Route 1  (should be Route 2)

Expected behavior
An expected behavior for a user would be that navigating back brings back to the previous routed page.
Workflow

Main    -- navigate to     --> Route 1
Route 1 -- navigate to     --> Route 2
Route 2 -- navigate to     --> Route 3
Route 3 -- location.back() --> guard returns true  --> Route 2
Route 2 -- navigate to     --> Route 3
Route 3 -- location.back() --> guard returns false --> Route 3
Route 3 -- location.back() --> guard returns true  --> Route 2  (expected)

Minimal reproduction of the problem with instructions
Plnkr

  1. Click button Nav to route1
  2. Click button Nav to route2
  3. Click button Nav to route3
  4. Click button Block Nav Back
  5. Click button Nav back
    • BOGUE: The location.back() routed on Route1 instead of Route2

Personnal investigation
After some investigation, I saw that in routerState$.then (router.ts line 752) this logic used when navigationIsSuccessful == false is pretty simply but it is the actual cause of this bug. Basically, when a deactivation guard is hit, the location of the browser is already changed to the previous route. Which means that when the guard returns false, the routerState$ runs his logic and calls resetUrlToCurrentUrlTree(). At this point we can see that we replace the state of the current location. But by doing this, we loose that route in the history which means that in my plunker, if we click the block nav back 3 times and then click the nav back we will actually kill the application.

What is the motivation / use case for changing the behavior?
This is for me a pretty big bug since a guard that returns false breaks alters the current routing history. In the case of our application this breaks the workflow and brings wrong business scopes to a user.

Please tell us about your environment:

Windows 10, NPM, Nodejs, Visual Studio 2015 (using nodejs for typescript compilation)

  • Angular version: 2.3.3

  • Browser: [ all ]

  • Language: [TypeScript 2.0.10 | ES5]

@saverett
Copy link

@saverett saverett commented Jan 13, 2017

A Router.back() method wouldn't cover using the browser back and forward buttons though, correct? That seems to be the predominant use case.

@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Jan 13, 2017

A Router.back() method wouldn't cover using the browser back and forward buttons though, correct?

yeah. So just returning history to the previous state is the only way I can think of here.

DzmitryShylovich added a commit to DzmitryShylovich/angular that referenced this issue Jan 14, 2017
DzmitryShylovich added a commit to DzmitryShylovich/angular that referenced this issue Jan 14, 2017
DzmitryShylovich added a commit to DzmitryShylovich/angular that referenced this issue Jan 14, 2017
DzmitryShylovich added a commit to DzmitryShylovich/angular that referenced this issue Feb 12, 2017
@michelherv
Copy link

@michelherv michelherv commented Feb 22, 2017

Hi,
I have the same problem in case of Activation Guard. Have I to create a new issue ?

  • Angular version: 2.4.6
  • Browser: all
  • Language: TypeScript 2.1.5
@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Feb 22, 2017

@michelherv yeah, reproduce on plunkr and file a new issue pls

@michelherv
Copy link

@michelherv michelherv commented Feb 22, 2017

@DzmitryShylovich done! You can found the description here #14645

@szh
Copy link

@szh szh commented Mar 30, 2017

This is still broken in Angular 4.0. Any plans to fix it?

@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Mar 30, 2017

@szh please don't spam everywhere. Pr is here #13922

DzmitryShylovich added a commit to DzmitryShylovich/angular that referenced this issue Mar 30, 2017
@CRodriguez25
Copy link

@CRodriguez25 CRodriguez25 commented May 16, 2017

Is there a work around for this by any chance?

@arutnik
Copy link

@arutnik arutnik commented May 24, 2017

I couldn't find any workaround inside my candeactivate class. Still waiting for the PR

@skaiser
Copy link

@skaiser skaiser commented May 26, 2017

It seems it should work like resolve does, no? That is, it doesn't navigate until canDeactivate cancels or confirms whether the component can be deactivated (or routed away from). Otherwise, I don't really understand what it's purpose is.

@CRodriguez25
Copy link

@CRodriguez25 CRodriguez25 commented May 26, 2017

Skaiser, its purpose is what you described, but there is a bug currently. A PR has been made to solve the issue, but until then the CanDeactivate guard breaks your history.

@alexeykostevich
Copy link

@alexeykostevich alexeykostevich commented Aug 14, 2017

Our team develops an application that uses browser history heavily to navigate through pages. Unfortunately, we have faced the same issue which causes a bit weird navigation experience for users.
The fix for this issue would be much appreciated!

@deepender87
Copy link

@deepender87 deepender87 commented Sep 27, 2017

Hey Guys
I am facing the same issue, can someone update, when it can be fixed. I am using angular 4.0 and router 4.0

@malthusyau
Copy link

@malthusyau malthusyau commented Oct 16, 2017

Hi all, what's the status on this issue? Or is there any workaround?

@awetstone56
Copy link

@awetstone56 awetstone56 commented Oct 23, 2017

I am also using Angular 4.0 and Router 4.0. Having this issue as well. This is a major bug and is a big setback for our application. Would be great if a workaround could be found or fix be made to this.

@caffeineinc
Copy link

@caffeineinc caffeineinc commented Oct 30, 2017

Still a problem, any ideas ?

@Kiwi15
Copy link

@Kiwi15 Kiwi15 commented Nov 16, 2017

Still have no fix ?

@patrickracicot
Copy link
Author

@patrickracicot patrickracicot commented Jan 9, 2018

Can someone give the state of this issue?

aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 25, 2020
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Feb 22, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Mar 15, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Mar 27, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
@photostu
Copy link

@photostu photostu commented Apr 2, 2021

Any updates on this? Inquiring minds want to know...

aahmedayed added a commit to aahmedayed/angular that referenced this issue Apr 8, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Apr 12, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Apr 12, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
@michaelmarcuccio
Copy link

@michaelmarcuccio michaelmarcuccio commented Apr 21, 2021

Is the Activate Guard also being worked on for the same issue here or are the changes just targeting Deactivation Guards?

@bmcdaniels
Copy link

@bmcdaniels bmcdaniels commented Apr 26, 2021

Is the Activate Guard also being worked on for the same issue here or are the changes just targeting Deactivation Guards?

I don't have an answer, but I want to also step in and +1 this point. Our setup calls for an activation guard to stop navigation to particular routes when a condition is met. The same exact problem detailed in this issue happens on this scenario also.

aahmedayed added a commit to aahmedayed/angular that referenced this issue Apr 27, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
@ChristophKinn
Copy link

@ChristophKinn ChristophKinn commented Apr 29, 2021

Why hasn't this been fixed yet? Is there any sign from the angular dev team?

aahmedayed added a commit to aahmedayed/angular that referenced this issue Apr 30, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
@JanneHarju
Copy link

@JanneHarju JanneHarju commented Apr 30, 2021

Can @aahmedayed say in what version this fix will be coming out? 12?

aahmedayed added a commit to aahmedayed/angular that referenced this issue May 1, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue May 1, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue May 1, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue May 5, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue May 30, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Jun 2, 2021
Since we can’t determine whether the user actually meant the “back” or
the “forward” using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before popstate is triggered and when the guard returns “false”.
So by using the `goTo` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
add the pageId field and use it instead of navigationId.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Jun 2, 2021
Since we can’t determine whether the user actually meant the `back` or
the `forward` using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before `popstate` is triggered and when the guard returns `false`.
So by using the `History#go` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
Add the `ngRouterPageId` in the `RestoredState`.
Add a solution to the navigation with `skipLocationChange`.
add a solution to the navigation with `replaceUrl`.
Add a solution to the navigation with `eager` `urlUpdateStrategy`
Add `cancelNavigationTransition` method to the router.
change the behavior of cancelNavigationTransition method.

fixes angular#13586
aahmedayed added a commit to aahmedayed/angular that referenced this issue Jun 2, 2021
Since we can’t determine whether the user actually meant the `back` or
the `forward` using the popstate event.
We could use this as the solution for the issue of getting the url back
to it’s initial state,
right before `popstate` is triggered and when the guard returns `false`.
So by using the `History#go` method,
we can bring the user back to the page where he is supposed to be after
performing the action.
Add the `ngRouterPageId` in the `RestoredState`.
Add a solution to the navigation with `skipLocationChange`.
add a solution to the navigation with `replaceUrl`.
Add a solution to the navigation with `eager` `urlUpdateStrategy`
Add `cancelNavigationTransition` method to the router.

fixes angular#13586
atscott added a commit to aahmedayed/angular that referenced this issue Jun 9, 2021
… is cancelled

We can’t determine whether the user actually meant the `back` or
the `forward` using the popstate event (triggered by a browser
back/forward)
so we instead need to store information on the state and compute the
distance the user is traveling withing the browser history.
So by using the `History#go` method,
we can bring the user back to the page where he is supposed to be after
performing the action.

implementation for angular#13586
alxhub added a commit that referenced this issue Jun 10, 2021
… is cancelled (#38884)

We can’t determine whether the user actually meant the `back` or
the `forward` using the popstate event (triggered by a browser
back/forward)
so we instead need to store information on the state and compute the
distance the user is traveling withing the browser history.
So by using the `History#go` method,
we can bring the user back to the page where he is supposed to be after
performing the action.

implementation for #13586

PR Close #38884
alxhub added a commit that referenced this issue Jun 10, 2021
… is cancelled (#38884)

We can’t determine whether the user actually meant the `back` or
the `forward` using the popstate event (triggered by a browser
back/forward)
so we instead need to store information on the state and compute the
distance the user is traveling withing the browser history.
So by using the `History#go` method,
we can bring the user back to the page where he is supposed to be after
performing the action.

implementation for #13586

PR Close #38884
@atscott
Copy link
Contributor

@atscott atscott commented Jul 1, 2021

Hi, quick update here:

  • We have the fix for this in the router code (#38884), but it's not yet exposed through the public API.
  • We're still running through tests and there is one known issue at the moment: #38884 (comment)
  • If you'd like to help test things out, you can enable the fix by putting this bit of code in the app component
// @ts-ignore: private option not yet exposed for public use
router.canceledNavigationResolution = 'computed';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment