Skip to content

feat(router): Cascading route support#16416

Closed
matthewerwin wants to merge 1 commit intoangular:mainfrom
snaptech:route-guard-flow
Closed

feat(router): Cascading route support#16416
matthewerwin wants to merge 1 commit intoangular:mainfrom
snaptech:route-guard-flow

Conversation

@matthewerwin
Copy link
Copy Markdown

@matthewerwin matthewerwin commented Apr 28, 2017

If you want the awesomely cool capability of doing things like yoursite.com/@<user|org> in your routes in Angular and having the name be an organization or an individual handled by separate modules (a la Twitter-style or Medium-style) you should definitely upvote this PR and help us get it merged!

This PR accomplishes:

Added cascading routes capability to the router, added new test cases.
Previous behavior resulted in dead routes (e.g. direct navigation to a
route with canActivate=>false resulted in blank page). Additionally this
feature improvement provides conditional routing based on params and other
information that could be used to dynamically enable/disable a route while
ensuring the user always falls through to a valid route.

Fall-through on routing tables instead of having routes themselves redirect is a well-established manner for handling permission-based routing (i.e. guard-based). This PR has been out since Angular 2.0 and is still not merged -- please help us get this done so it isn't an ongoing maintenance task to update it with every version change.

This PR passes all latest tests and should be backwards compatible.

Please check if the PR fulfills these requirements

What kind of change does this PR introduce? (check one with "x")

[X] Bugfix
[X] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior? (You can also link to an open issue here)
#16211

Current behavior results in a dead link/route when routes are inaccessible due to canActivate rejecting the promise or returning false.

Router attempts to recover state to originating route state when canActivate or other preactivation guards fail (e.g. data loading guard). This either leaves the user clicking a link that does nothing, a blank page if the URL is hit directly (bookmark, external site link, page refresh while canActivate changes since original load).

What is the new behavior?
New behavior falls through to other valid routes including the default '**' route to give the user a 404 or other message. This allows more sophisticated routing scenarios based on conditionality as well as elegant handling for no-access scenarios that do not redirect.

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[ X ] No

All tests passed. New tests written to cover the fall through scenarios.

If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...

Other information:

@googlebot
Copy link
Copy Markdown

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@matthewerwin
Copy link
Copy Markdown
Author

I signed it! (CLA)

@googlebot
Copy link
Copy Markdown

CLAs look good, thanks!

@matthewerwin
Copy link
Copy Markdown
Author

FYI ci/circleci is failing the LINT on the commit message but that is for someone else's commit that's already on angular/4.0.x (INVALID COMMIT MSG: "release: cut the 4.0.1 release").

@matthewerwin
Copy link
Copy Markdown
Author

@vicb do you have any commentary on this since you've been involved quite a bit in the router?

on a separate note while I was in there I noticed that exceptions were used during the normal paths of execution vs architecting to return a complex object or using an observable/promise reject/error. I'm up for tackling that re-factoring if you guys are on board once this request is merged.

@the-destro
Copy link
Copy Markdown

Super excited for this as I am converting a legacy application and initially thought this was how Guards worked so I am currently in a bind.

@matthewerwin
Copy link
Copy Markdown
Author

@the-destro if you get a chance can you pull this into your project and verify it works as you expect for your particular use-case? If not, I'd like to catch any specific conditions you're addressing while this pull request is pending merge.

Here is what you run to get this pre-merged package running:

npm uninstall @angular/router --save
npm install @angular/router --registry https://myget.org/F/snaptech/npm --save

That will run this build to test then anytime you run "npm install" after it will just revert to the main branch or you can re-run uninstall and install without specifying the separate registry.

@the-destro
Copy link
Copy Markdown

@alexbyk
Copy link
Copy Markdown
Contributor

alexbyk commented Jun 4, 2017

As I understood from the description, this can be used insted of #14515 right?

@matthewerwin
Copy link
Copy Markdown
Author

@alexbyk thanks for referencing that. Yes, this pull request solves #14515

@the-destro
Copy link
Copy Markdown

Is there an ETA on when this can be merged/released?

@matthewerwin
Copy link
Copy Markdown
Author

@vicb what's the word? Anything I can do to get this merged in more quickly?

@the-destro
Copy link
Copy Markdown

@vicb @mhevery Any update on feedback or merging? Maybe for Angular 5?

@jasonaden jasonaden self-requested a review July 13, 2017 21:40
@jasonaden jasonaden self-assigned this Jul 13, 2017
Copy link
Copy Markdown
Contributor

@jasonaden jasonaden left a comment

Choose a reason for hiding this comment

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

Thanks for this PR and this is a great feature. Sorry for the delay on getting back to you.

I've added a couple comments. There's also been changes requiring a rebase in order to merge this. Please go ahead and address these, then we can get this merged.

Comment thread packages/router/src/apply_redirects.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
@jasonaden jasonaden added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Jul 13, 2017
@matthewerwin
Copy link
Copy Markdown
Author

@jasonaden I squashed the commits and rebased against 4.0.x for this pull request. I also fixed the errors (The 4.0.x branch on angular errors on ./build.sh due to some things that appeared to be refactored/fixed in 4.1.x). I figured everyone would prefer 4.0.x builds without issues when pulling that branch.

Separately I've also created pull requests for 4.2.x and 4.3.x and all of them use the purely rxjs style flow instead of the recursive function call that was in the initial review. That should make comparisons between the existing guard code and the cascading flow guard code very easy to identify. I cleaned up everything you mentioned in the Code Review & with the change to maintain the recognize( ) signature that also reduced the total changes in the pull request. :-)

@matthewerwin matthewerwin changed the title feat(router): Cascading route support when route guard => false feat(router): Cascading route support (4.0.x) Jul 17, 2017
Copy link
Copy Markdown
Contributor

@jasonaden jasonaden left a comment

Choose a reason for hiding this comment

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

You probably saw the note in one of the other PRs, but take a look at the new comment about mergeMapTo and a piece of code that should fix it.

Then let's get this rebased against master branch and the PR should also be against master as that's where all merging happens (we create the 4.x versions by cherry picking the appropriate commits from master).

Hopefully you could do that today as I would like to go over this PR with someone else tomorrow and see if we can get you a final review.

Comment thread packages/router/src/apply_redirects.ts Outdated
@matthewerwin
Copy link
Copy Markdown
Author

@jasonaden I'll make it happen today. Will issue the new PR against master and test out if the suggested change works.

@matthewerwin matthewerwin changed the base branch from 4.0.x to master July 18, 2017 04:09
@matthewerwin
Copy link
Copy Markdown
Author

@CaerusKaru thanks for the update -- this PR has been lingering forever and not sure why. We made all the changes originally requested by the angular team and the interior of the router hasn't changed much other than a minor refactor in all the time since the PR was initially entered.

Let me know how we can support getting this moved forward so I don't have to maintain separate binaries for our platforms.

@matthewerwin
Copy link
Copy Markdown
Author

@kara @IgorMinar happy new year! What steps do we need to take to get this on track for review?

@matthewerwin
Copy link
Copy Markdown
Author

@atscott I noticed to pull approve not request 22 days ago...any ETA?

@josh18
Copy link
Copy Markdown

josh18 commented Mar 17, 2020

This would be super useful if you are creating a new version of a feature and you want to keep it on the same route but have it protected by a feature flag guard.

@matthewerwin
Copy link
Copy Markdown
Author

@josh18 exactly. Now if they would just finish the review and merge it already 🤦‍♂️

Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/test/recognize.spec.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/apply_redirects.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Copy link
Copy Markdown
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.

In addition to the comments on the code, the default behavior for routing should remain the same. That is, if the first matched navigation fails (i.e. because of a guard returning false), then the navigation is cancelled. The cascading/fallback approach should be protected by some configuration (like in the router config).
As an example, applications could very well rely on the fact that if you are in one place in the application and you attempt to navigate to somewhere that is prevented by a guard, the navigation is cancelled. Instead, this PR could cause the navigation to fall back to the ** route that displays something like 'page not found'. While this may be desirable behavior for some/many/most people, changing the default behavior would be undesirable to others.

Edit: Maybe a better example is a guard which prevents you from leaving a page because you have unsaved changes. You would want this navigation to continue to be cancelled and definitely not want this navigation to fall back to the ** route.

@matthewerwin
Copy link
Copy Markdown
Author

@atscott thank you for the thorough review. I will have a chance to go through in detail over the next few days and respond to each comment or resolve as suggested.

As to your last separate remark on backwards compatibility that’s easily done with a flag and is actually a reason why recognize.ts was modified...if it returns a single result (which then fails) then behavior should be identical to the original code but I will double check it.

Related to the choice of two dimensional array that is a matter of per iteration and cumulative selection of routes. The design is to pipe all matching routes via the observable to allow fall-through evaluation in navigate. So likely that will need to remain the same but I will look at it very thoroughly & beef up some test cases as needed to trial other techniques if necessary.

Look forward to making this as robust as necessary so if you have extra test cases or other things to assist I’m happy to wrap into a secondary branch to verify before folding them in here

Thanks a million! Will turn around on my side as quickly as I can.

@matthewerwin
Copy link
Copy Markdown
Author

@atscott the PR review modifications are in & I also did a force push b/c CircleCI required it. I am however seeing a failure in the AIO router examples project on an E2E test scenario here.

I'll be able to look at it tomorrow but in the meantime if you can review and verify that the feedback you gave on the PR was interpreted accurately that would be greatly appreciated! If you happen to see what's going on with that E2E test lmk...I'm just too tired to see it clearly at the moment.

Comment thread packages/router/src/recognize.ts Outdated
Copy link
Copy Markdown
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.

Again, thank you so much for taking the feedback and iterating on the approach here. I think we're much closer now to an implementation that we can easily explain and defend. On those lines, thinking about what it would take to get this submitted:

If it returns false, the navigation process stops and the user stays put.

  • The default behavior in this PR is for cascade to be true. There needs to be a config which controls this and the default should be cascade === false so we don't break existing router setups.

Comment thread packages/router/src/operators/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@matthewerwin
Copy link
Copy Markdown
Author

matthewerwin commented Mar 26, 2020

Again, thank you so much for taking the feedback and iterating on the approach here. I think we're much closer now to an implementation that we can easily explain and defend. On those lines, thinking about what it would take to get this submitted:

If it returns false, the navigation process stops and the user stays put.

  • The default behavior in this PR is for cascade to be true. There needs to be a config which controls this and the default should be cascade === false so we don't break existing router setups.

@atscott thanks for the fast turnaround time on the review. I added the code to make cascading choice via config and default to cascade = false to maintain original behavior and avoid potentially causing breaking changes.

I also added some documentation per your request & updated the router API to include the enable/disable of the cascade feature via the ExtraOptions available on the router config. Please advise if I missed anything.

Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/operators/recognize.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/router/test/integration.spec.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/router.ts Outdated
@atscott
Copy link
Copy Markdown
Contributor

atscott commented Mar 30, 2020

global presubmit passed, meaning the default config (i.e. the current router behavior) is fully backwards compatible.

I believe the lint failure is unrelated. A rebase should resolve it. Would you mind investigating the other two failures and squashing the commits into one?

@matthewerwin
Copy link
Copy Markdown
Author

@atscott I went ahead and rebased and squashed to a single commit. The lint failure was due to an earlier rebase when lint failures were present in master and appears resolved in the latest rebase.

The other failures you refer to are also happening on master branch and appear to be Ivy integration tests. I pulled master branch and re-built locally and ran all tests and see the same failures on that branch as here. I would resolve them if I knew how but Ivy integration is beyond the scope of my experience on this project. My guess is that they're added by 3rd parties as test cases to Ivy to obtain backwards compatibility or related to unresolved edge cases.

@CaerusKaru
Copy link
Copy Markdown
Member

@matthewerwin Looks like you just need to update the bundle size limits in this file.

Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/recognize.ts Outdated
Comment thread packages/router/src/router.ts Outdated
Comment thread packages/router/src/router_module.ts Outdated
Comment thread packages/router/src/router_module.ts Outdated
Comment thread packages/router/src/router_module.ts Outdated
Comment thread packages/router/src/router_module.ts Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am generally not a fan of multiple syntaxes to achieve the same result. It may be confusing to users who expect different behavior for different options. I can see how cascade: 'cascade' looks silly, so maybe the options should be only boolean true and 'legacy', without false and 'cascade'.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Repeating a property name is common in HTML. I don't think this harms anything here and is in alignment with the other field settings. In the event the behavior is switched to default cascading in a future version it would also be odd not to support false -- if it's going to be more restrictive then makes sense to limit to just T/F though I'm not sure how the current setup does any harm as its converted by setupRouter/setupTestRouter before it hits any conditional router code.

Comment thread packages/router/test/recognize.spec.ts Outdated
Copy link
Copy Markdown
Contributor

@IgorMinar IgorMinar left a comment

Choose a reason for hiding this comment

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

Hi @matthewerwin, first of all thanks for your contribution and apologies for us not being more responsive here.

It sucks that this PR has been stuck in PR "review purgatory" for this long. I see that Joost is looking at it now which is somewhat good. But I also see that this PR changes the router API in a non-trivial way so we’ll need to sit down and think through the implications of this change. There is no design attached to this change, so doing the design retroactively kind of sucks for all of us. I can’t make promises about this landing, but we can spend a bit of time trying to understand what is driving this change and if the change is aligned with where we’d like to go.

@matthewerwin
Copy link
Copy Markdown
Author

matthewerwin commented Apr 3, 2020

@IgorMinar thanks for taking the time to post. @JoostK and @atscott both provided some great feedback these past weeks which has all been incorporated.

I understand what you're staying about non-trival new behavior. Take a look at the latest comparisons though and you will see that this is restricted to the presence of a cascade property on the router config. The changes to router.ts were also pared back substantially to reduce the surface area as well.

A last note about this is that actually the number of line changes is a bit deceiving. The guard block was all moved into a concatMap, the recognize.ts logic was moved into a loop, and a couple others like this result in the diff showing a lot of changes but much of that is essentially whitespace changes

Copy link
Copy Markdown
Contributor

@kapunahelewong kapunahelewong left a comment

Choose a reason for hiding this comment

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

Just a few style suggestions. FYI, @matthewerwin, #35566 on the router doc is hopefully close to landing. Thanks for updating the doc too!

Edit:
Reviewed-for: global-docs-approvers

Comment thread aio/content/guide/router.md Outdated
Comment thread aio/content/guide/router.md Outdated
Comment thread aio/content/guide/router.md Outdated
Comment thread aio/content/guide/router.md Outdated
kapunahelewong
kapunahelewong previously approved these changes Apr 17, 2020
Added cascading routes capability to the router, added new test cases.
Previous behavior resulted in dead routes (e.g. direct navigation to a
route with canActivate=>false resulted in blank page). Additionally this
feature improvement provides conditional routing based on params and other
information that could be used to dynamically enable/disable a route while
ensuring the user always falls through to a valid route.

This PR passes all latest tests and should be backwards compatible.
@petebacondarwin
Copy link
Copy Markdown
Contributor

@atscott and @IgorMinar - what are we going to do about this PR? It is now very stale and old. Perhaps it is not something we will ever integrate? Or perhaps it can be updated and merged? Either way it can't get into 12.0.x, so it would now be targeting 12.1.0 or later.

@matthewerwin
Copy link
Copy Markdown
Author

I’m happy to get it rebased in for latest. I feel like every time the internal team gets ready to merge it though there is some distraction. We are also very busy on our side to keep going through the process to prepare to merge only to have it fall off priority for v10, v11 or whatever

Just need someone to champion it on the Angular team to help us make sure it goes all the way through to save us all a lot of time. Let me know who I can work with and we will get this rolled up this week.

@atscott
Copy link
Copy Markdown
Contributor

atscott commented Jun 14, 2022

We recently landed the CanMatch feature which enables cascading routes through an additional guard on a Route. CanMatch doesn’t solve the “blank screen” on initial navigation issue, but that can be addressed with a another solution as well (partially addressed by #46026 and will be followed up with a feature to configure this behavior for other cases). The combination of the two should satisfy the use-cases that this PR was aiming to address.

While the implementation we went with in the end differed from the approach taken here, the attention and feedback from this PR was a major factor in driving discussions and ultimately the solution to these use-cases.

@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: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews area: router cla: yes feature Label used to distinguish feature request from other issues risk: medium target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.