Skip to content

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

Closed
aahmedayed wants to merge 1 commit into
angular:masterfrom
aahmedayed:fix-deactivation-guard-breaks-routing-history
Closed

aahmedayed wants to merge 1 commit into
angular:masterfrom
aahmedayed:fix-deactivation-guard-breaks-routing-history

Conversation

@aahmedayed

@aahmedayed aahmedayed commented Sep 17, 2020 •

Copy link
Copy Markdown
Contributor

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

@aahmedayed
aahmedayed force-pushed the fix-deactivation-guard-breaks-routing-history branch from e2b10a5 to add1b74 Compare September 17, 2020 15:49

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

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.

Comment thread packages/common/src/location/location.ts Outdated
Comment thread packages/common/testing/src/location_mock.ts Outdated
Comment thread packages/common/src/location/location_strategy.ts Outdated
Comment thread packages/common/src/location/platform_location.ts Outdated
@aahmedayed

Copy link
Copy Markdown
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

aahmedayed commented Sep 17, 2020 •

Copy link
Copy Markdown
Contributor Author

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

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@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
aahmedayed requested a review from atscott September 23, 2020 17:57
@aahmedayed

Copy link
Copy Markdown
Contributor Author

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

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

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

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@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

atscott commented Sep 23, 2020

Copy link
Copy Markdown
Contributor

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

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@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
@aahmedayed

aahmedayed commented May 5, 2021 •

Copy link
Copy Markdown
Contributor Author

Hi @atscott i made the requested changes.
Also, i created a new commit e3da2c6eb3 but i'm not sure if we should add it in a new Pr?

@atscott

atscott commented May 6, 2021

Copy link
Copy Markdown
Contributor

@aahmedayed Thanks, I'll take another look today. I think we should not include e3da2c6 because it would make this TODO even harder to resolve. It might be that it's something that doesn't make sense to change anymore but we should limit the changes in this PR to what's absolutely necessary to accomplish our goal here.

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Another round of comments with one noticeable bug.

Sorry, I know this has been a ton of work for you but you're doing a really great job here! This change is incredibly complex with a lot of moving parts and edge cases to test.

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/events.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would prefer not adding ngRouterPageId here because it makes it more public than I would want. Accessing it is still possible even without it because of the {[k: string]: any}. I don't think navigationId should ever really have been included here because it makes the public API more confusing since "navigationId" wasn't meant to be public in this way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe this was missed in one of the squashes. Let's remove ngRouterPageId here so we don't change the public API.

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you add the else case with a comment as to why it's a no-op?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unresolving. Please add the else case and add a comment as to why nothing is done there (if update is not 'eager', then the navigation was cancelled before any browser url change so nothing needs to be restored).

Comment thread packages/router/src/router_module.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/common/testing/src/location_mock.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
@aahmedayed

Copy link
Copy Markdown
Contributor Author

Hi @atscott, no problem at all, I’ll take care of it ASAP.

@atscott

atscott commented May 25, 2021

Copy link
Copy Markdown
Contributor

Hi @aahmedayed, have you gotten a chance to address the last round of feedback? I have some ideas that would allow us to merge this as a non-breaking change after you've pushed your latest updates.

@aahmedayed

Copy link
Copy Markdown
Contributor Author

Hi @atscott, I'll try to finish with the requested changes this week at the latest.

@aahmedayed

aahmedayed commented Jun 1, 2021 •

Copy link
Copy Markdown
Contributor Author

Hi @atscott, I'm done with the requested changes, but i think we can't do this #38884 (comment) change now because the test_ivy_aot failed and the error is that cannot find name RestoredState in router.d.ts.

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@aahmedayed thanks for the updates! I'll take a look at the changes this week. No problem on the @internal comment.

My plan for landing this change is:

  • Submit the change first without a public API option so we can continue to test and offer the solution to the interested parties on the bug as a private preview (the option in the Router will be set to @internal but users can opt-in with (Router as any).canceledNavigationResolution = 'computed'). We can consider a follow-up change to add it as public in a minor version.
  • Move the changes to location_mock into a local copy used by the router tests. This avoids making this a breaking change and we can remove it when #41730 is submitted.
  • clean up the commits: squash most/all of them into a single commit and update the commit message(s)
  • Move the commit that updated the navigationId and pageId to be in the ng namespace to a follow-up PR (again to avoid making this a breaking change). I like this change but it would prevent us from submitting this until v13.

Sorry for requesting reverts to changes I had you do; we should get them merged eventually, but getting this PR submitted sooner rather than later (in v13) seems like a good idea. Do you mind if I make those changes and push up to your branch as a co-author or would you like to do the updates yourself?

Comment thread goldens/public-api/router/router.d.ts Outdated
@aahmedayed

Copy link
Copy Markdown
Contributor Author

Ok @atscott there is no probleme, I start making those changes right now. But I'm not sure I understand the second point correctly. Can you please provide me with more details ? Thank you.

@atscott

atscott commented Jun 1, 2021

Copy link
Copy Markdown
Contributor

@aahmedayed - Absolutely. There are changes here that depend on your updates to the back/forward methods in SpyLocation. Those changes will be considered a breaking change and can't be submitted until v13. To get around that, we can copy the whole implementation of SpyLocation to a class in the router's integration.spec.ts and make the update there (and also add {provide: Location, useClass: RouterSpyLocation} to the providers in the beforeEach). That way we can submit this change now and then later update the router test to point to the real SpyLocation in v13 after #38884 is submitted.

@aahmedayed

Copy link
Copy Markdown
Contributor Author

Ah ok! I understand, thank you @atscott

@aahmedayed

Copy link
Copy Markdown
Contributor Author

Hi @atscott - I'm done with the requested changes.

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking good. A few more minor comments. Also, can you amend the commit message to be:

refactor(router): compute correct history restoration when navigation 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.

We should change the scope of this commit to be a refactor since we haven't provided a public API for the fix (so nothing was actually fixed yet). We should also remove the fix # comment since, again, we haven't made the fix available.

Comment thread packages/router/src/events.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe this was missed in one of the squashes. Let's remove ngRouterPageId here so we don't change the public API.

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread goldens/size-tracking/integration-payloads.json Outdated
@aahmedayed

Copy link
Copy Markdown
Contributor Author

Hi @atscott - I'm done with the requested changes.

@atscott atscott left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM with two minor comments on the state restoration function.

reviewed-for: public-api
reviewed-for: size-tracking

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unresolving. Please add the else case and add a comment as to why nothing is done there (if update is not 'eager', then the navigation was cancelled before any browser url change so nothing needs to be restored).

@AndrewKushnir

Copy link
Copy Markdown
Contributor

Presubmit.

@jessicajaniuk jessicajaniuk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM 🍪
reviewed-for: size-tracking

Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@aahmedayed

Copy link
Copy Markdown
Contributor Author

Hi guys, i have finished the changes that have been asked of me, but i haven’t had the chance to work on the docs since i’m having a very busy week.
@atscott if you don’t mind, could you please take it from here to fix the documentations so that i don’t cause a delay in the workflow. Thank you in advance.

… 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

@AndrewKushnir AndrewKushnir left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@aahmedayed thanks for addressing the feedback, the changes look good 👍

@atscott

atscott commented Jun 9, 2021

Copy link
Copy Markdown
Contributor

another presubmit for a sanity check

@aahmedayed

Copy link
Copy Markdown
Contributor Author

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

@atscott

atscott commented Jun 18, 2021

Copy link
Copy Markdown
Contributor

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

atscott commented Jul 2, 2021 •

Copy link
Copy Markdown
Contributor

@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.

@aahmedayed

Copy link
Copy Markdown
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.

@angular-automatic-lock-bot

Copy link
Copy Markdown

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.

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.

6 participants