angular: Router links in `@defer` blocks not having correct relative route
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
17.2.1
Description
A RouterLink with a route of ./
should route to the activated route. (eg. <a [routerLink]="'./'" >Link One</a>
). But when it is in a defer block, and it is a sibling of a component that imports from material it routes to the app root.
So
@defer(on timer(1000)) { <has-material/> <a [routerLink]="'./'" >Link One</a> }
routes to the root.
Reproduction
StackBlitz link: https://stackblitz.com/edit/stackblitz-starters-prh2zk Steps to reproduce:
- Have a RouterLink in a defer block that has a relative route
- Give it a sibling that imports from Material
Expected Behavior
The router link route should be relative to the current route.
Actual Behavior
The router link route is from the root. The ‘ActiveRoute’ seems to be incorrect.
Environment
- Angular: >= 17.2.4
- CDK/Material: 17.2.2
- Browser(s): edge, chrome
- Operating System (e.g. Windows, macOS, Ubuntu): Windows
About this issue
- Original URL
- State: closed
- Created 4 months ago
- Reactions: 4
- Comments: 23 (9 by maintainers)
Commits related to this issue
- refactor(router): use real `EnvironmentInjector` in the `RouterOutlet` class Currently, the `RouterOutlet` class uses a custom injector implementation to provide route-specific tokens (such as `Activ... — committed to AndrewKushnir/angular by AndrewKushnir 4 months ago
- refactor(router): use real `EnvironmentInjector` in the `RouterOutlet` class Currently, the `RouterOutlet` class uses a custom injector implementation to provide route-specific tokens (such as `Activ... — committed to AndrewKushnir/angular by AndrewKushnir 4 months ago
- refactor(router): use real `EnvironmentInjector` in the `RouterOutlet` class Currently, the `RouterOutlet` class uses a custom injector implementation to provide route-specific tokens (such as `Activ... — committed to AndrewKushnir/angular by AndrewKushnir 4 months ago
- fix(core): establish proper injector resolution order for `@defer` blocks This commit updates the `@defer` logic to establish proper injector resolution order. More specifically: - Makes node inject... — committed to AndrewKushnir/angular by AndrewKushnir 3 months ago
- fix(core): establish proper injector resolution order for `@defer` blocks (#55079) This commit updates the `@defer` logic to establish proper injector resolution order. More specifically: - Makes no... — committed to angular/angular by AndrewKushnir 3 months ago
- fix(core): establish proper injector resolution order for `@defer` blocks (#55079) This commit updates the `@defer` logic to establish proper injector resolution order. More specifically: - Makes no... — committed to atscott/angular by AndrewKushnir 3 months ago
- fix(core): establish proper injector resolution order for `@defer` blocks (#55079) This commit updates the `@defer` logic to establish proper injector resolution order. More specifically: - Makes no... — committed to ilirbeqirii/angular by AndrewKushnir 3 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token dynamica... — committed to AndrewKushnir/angular by AndrewKushnir 2 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token dynamica... — committed to AndrewKushnir/angular by AndrewKushnir 2 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token dynamica... — committed to AndrewKushnir/angular by AndrewKushnir 2 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token dynamica... — committed to AndrewKushnir/angular by AndrewKushnir 2 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks (#55374) `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token... — committed to angular/angular by AndrewKushnir 2 months ago
- fix(core): make `ActivatedRoute` inject correct instance inside `@defer` blocks (#55374) `RouterOutlet` uses a unique injector logic that returns a value that correspond to the `ActivatedRoute` token... — committed to JeanMeche/angular by AndrewKushnir 2 months ago
Hello @AndrewKushnir, thanks a lot for the fix. I can confirm on my side that it is working now!
@Yberion @bboyle thanks for providing a repro. We’ve identified the root cause and I’ve created a PR with a fix: https://github.com/angular/angular/pull/55374. I will update this thread once the mentioned PR lands.
@bboyle @atscott We just rolled out with 17.3 and we are having a similar issue. Sometimes the route is ‘stuck’ on an old value. I am also struggling to figure out what causes that behavior and how to reproduce.
FYI, the fix from PR https://github.com/angular/angular/pull/55374 was merged and released as a part of v17.3.6. Please let us know if the problem still exists after updating to v17.3.6.
This bug seems like the previous bug. Using a defer block around a component that imports ‘something’ causes issues with the ActivatedRoute in that component.
Hello, it seems that I have the same problem there, I wanted to use defer blocks to load a different layout depending on the result of the
BreakpointObserver
, both having a router outlet for child pages.The problem occur with a component from
Angular Material
, it seems that the router andActivatedRoute
are not “synchronized” anymore.npm i
npm run start
go to page1 from home
page2
page 1
home
page2
home
And it is at this point that we get different results from the router and the snapshot.
The problem appears in the component that is defered
LoaderComponent
, no problem in theSomepageComponent
(check console logs).The problem disappears if I remove
MatButtonModule
fromLoaderComponent
OR if I remove the defer block fromSomepageComponent
.I’m on Angular
17.3.4
FYI I don’t have
RouterModule
imported anywhere in my app (standalone component migration was completed). Will keep working on reproducing the issue.@AndrewKushnir is out of office until next week.
I made the reproduction a bit smaller: https://stackblitz.com/edit/stackblitz-starters-uxf7bm I found that removing the
RouterModule
imports of the component in the defer block makes it work.Looks good. Thanks for working on it!
@NachmanBerkowitz thanks for reporting the issue.
We found the origin of the problem and we are working on a fix. There are a couple first PRs created with the necessary refactoring (#54903 and #54907) and we’ll need create a couple more PRs that would fix the issue. There is no exact ETA at this moment, but the final fix would likely be available in one of the 17.3.x releases.
I could narrow it down a bit:
The
ActivatedRoute
has an undefined route when in a defer block and having a import (whatever import).repro: https://stackblitz.com/edit/angular-repro-54864
Likely related to the injector construction of
@defer
.This seems like either a framework or router issue. Material doesn’t use the router at all. Transferring to the framework repo.