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

refactor(router): compute correct history restoration when navigation is cancelled #38884

Conversation

aahmedayed
Copy link
Contributor

@aahmedayed aahmedayed commented Sep 17, 2020

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.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Copy link
Contributor

@atscott atscott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @aahmedayed - this looks promising! What do you think of breaking this up into two PRs? I think a PR to add go/goTo as a feature separately would be valuable on its own. Then have a separate PR to fix the routing history with guards. This would make the review process much easier.

I haven't reviewed the changes to the router yet, as that will take a little more time and careful review.

packages/common/src/location/location.ts Outdated Show resolved Hide resolved
packages/common/testing/src/location_mock.ts Outdated Show resolved Hide resolved
packages/common/src/location/location_strategy.ts Outdated Show resolved Hide resolved
packages/common/src/location/platform_location.ts Outdated Show resolved Hide resolved
@aahmedayed
Copy link
Contributor Author

Hi @atscott - thank you for your review, and yes i think it’s a good idea to have the solution into two PR’s.
I’ve already started to work on it.

@aahmedayed
Copy link
Contributor Author

aahmedayed commented Sep 17, 2020

@atscott - i creat a new feature PR #38890 for the go/goTo methods

packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
@aahmedayed aahmedayed force-pushed the fix-deactivation-guard-breaks-routing-history branch 3 times, most recently from 6e7416a to 894f5d0 Compare September 23, 2020 15:30
@aahmedayed
Copy link
Contributor Author

Hi @atscott - I’ve already finished the adjustments.

Copy link
Contributor

@atscott atscott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking better :) I've added another round of comments.

I think you may have missed one of my comments about a request to add some more testing around the history in some scenarios:

  • An error during navigation (this was why we moved some logic to finalize)
  • a route being cancelled by a canLoad guard
  • a route being cancelled by a resolver returning EMPTY

packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
packages/router/src/router.ts Show resolved Hide resolved
packages/router/src/router.ts Outdated Show resolved Hide resolved
@ngbot ngbot bot modified the milestone: needsTriage Sep 23, 2020
@atscott atscott added the target: major This PR is targeted for the next major release label Sep 23, 2020
@atscott
Copy link
Contributor

atscott commented Sep 23, 2020

Adding the breaking change flag since this would break tests that do assertions on the state object.

packages/router/src/router.ts Outdated Show resolved Hide resolved
@aahmedayed aahmedayed force-pushed the fix-deactivation-guard-breaks-routing-history branch 7 times, most recently from 4a1fb91 to 524762e Compare September 27, 2020 09:59
@atscott atscott added action: presubmit The PR is in need of a google3 presubmit and removed action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews labels Jun 9, 2021
@aahmedayed
Copy link
Contributor Author

You're the most welcome @AndrewKushnir and thanks a lot @atscott

@atscott atscott added action: merge The PR is ready for merge by the caretaker and removed action: presubmit The PR is in need of a google3 presubmit labels Jun 9, 2021
@alxhub alxhub closed this in efb440e Jun 10, 2021
alxhub pushed a commit that referenced this pull request 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 commented Jun 18, 2021

Hi @aahmedayed, I had a thought tonight about this and realized we missed a pretty important scenario. It's very common for guards to schedule a new navigation before returning false. This could be a bit of an issue since history.go causes a popstate event which will schedule another navigation that will compete with the one triggered by the guard. What we might want to do in the finalize/cancelNavigationTransition is to treat this scenario (!completed && !errored) more like what's done when a guard returns a UrlTree. This logic is here and if you look through it, you'll see that it's treated as a redirect and resetStateAndUrl is not called.

A specific scenario where this doesn't work:

  1. urlUpdateStrategy: 'eager'
  2. navigate to /a
  3. navigate to /b
  4. click back to go to /a, which has a canActivate guard that navigates to /
    ===> expectation: end on / but we end on /b instead because of the history.go call that triggers a navigation.

There might be other scenarios I haven't thought of. I'll be on vacation until July. If you get a chance to take a look and send a PR before then, I can review it when I'm back. Otherwise I'll see about investigating other scenarios, making some adjustments to the code, and then start thinking about making the option public opt-in.

@atscott
Copy link
Contributor

atscott commented Jul 2, 2021

@aahmedayed I've been working with this more today and I'm a little concerned that this won't work in the end. We can always figure out how to handle things when navigations go through the back/forward buttons or through router.navigate. However, navigations from outside of that can't be handled well (Location.go, any regular href on the page, browser navigation with the navbar, AngularJS/Angular hybrid apps where navigations come from AngularJS). All of these situations could result in something worse than what's already in place because these won't have the correct routerPageId and the computation for history.go will be incorrect.

Edit: Playing with this in a real application, it seems like browsers retain information about the origin of the navigation. This means that if I am going between pages that cross a manual navigation boundary, it treats that as a non-SPA navigation so the Router won't try to compute the history.go delta. I'll keep testing things out, but this is a bit more promising.

atscott added a commit to atscott/angular that referenced this pull request Jul 2, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 2, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 2, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 7, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 7, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 7, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
atscott added a commit to atscott/angular that referenced this pull request Jul 8, 2021
When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in angular#38884 (comment)
@aahmedayed
Copy link
Contributor Author

HI @atscott - Sorry for the delay I’m taking some days off, I’ll be back Monday. Thank you for all the fixes you made.

AndrewKushnir pushed a commit that referenced this pull request Jul 9, 2021
…#42751)

When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in #38884 (comment)

PR Close #42751
AndrewKushnir pushed a commit that referenced this pull request Jul 9, 2021
…#42751)

When another navigation is triggered during an in-process navigation and
the `canceledNavigationResolution` is `'computed'`, we should not
attempt to restore the browser history using `history.go`. Doing that
would trigger a third navigation through the router which would conflict
with the new navigation that we were trying to process. Instead, we
treat this as a redirect and skip the history restoration attempt. This
acts similarly to returning `UrlTree` from a guard.

Fixes issue described in #38884 (comment)

PR Close #42751
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Aug 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: router cla: yes target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants