-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
fix(router): lazy loaded modules without RouterModule.forChild() won't cause infinite loop #36605
Conversation
Hi @martinsik, thanks for another router fix! Can you update the commit message to match what you have in the "current behavior" section (without the stackblitz link) and also add a similar comment to the |
09504bd
to
9b8c49c
Compare
Commit message updated. |
Presubmit is showing internal test failures when running with Ivy. Needs more investigation. |
@atscott Hey, pls do you have any update on this :)? |
return new LoadedRouterConfig( | ||
flatten(module.injector.get(ROUTES)).map(standardizeConfig), module); | ||
flatten(module.injector.get(ROUTES, undefined, InjectFlags.Self)).map(standardizeConfig), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flatten(module.injector.get(ROUTES, undefined, InjectFlags.Self)).map(standardizeConfig), | |
flatten(module.injector.get(ROUTES, undefined, InjectFlags.Self|InjectFlags.Optional)).map(standardizeConfig), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failure that was found happened because your test is only verifying behavior on the preloader, which has a catchError
line
return fn().pipe(catchError(() => of(null))); |
Your change introduces a newly thrown error that would have otherwise not happened. The following test will fail if added to the integration.spec.ts
(please add this test):
@NgModule({})
class EmptyModule{}
fit('should allow navigation to modules with no routes', fakeAsync(() => {
(TestBed.inject(NgModuleFactoryLoader) as SpyNgModuleFactoryLoader).stubbedModules = {
empty: EmptyModule,
};
const router = TestBed.inject(Router);
const fixture = createRoot(router, RootCmp);
router.resetConfig([{path: 'lazy', loadChildren: 'empty'}]);
router.navigateByUrl('/lazy');
advance(fixture);
}));
I'm not exactly sure what the implications are for this. My understanding of the change you're making is that previously, navigation to this route would have inherited the parent routes. Even though it's infinitely circular, the problem wouldn't really surface unless there was preloading involved. I don't know exactly how this type of route is used so I don't know if the InjectFlags.Self
change negatively affects how that works. (Edit: potentially, this is something that enables a hybrid app to handle the route in AngularJS and also do some work on the Angular side, but that's just a shot in the dark.)
Please add the InjectFlags.Optional
and I'll run more tests to see if anything comes up. Thank you!
9b8c49c
to
5d8dcd3
Compare
@atscott Hi, I addressed what you mentioned above and also added the test you made into |
5d8dcd3
to
5f201f2
Compare
…t cause an infinite loop When loading a module that doesn't provide `RouterModule.forChild()` preloader will get stuck in an infinite loop and throw `ERROR Error: Maximum call stack size exceeded.` The issue is that child module's `Injector` will look to its parent `Injector` when it doesn't find any `ROUTES` so it will return routes for it's parent module instead. This will load the child again that returns its parent's routes and so on. Closes angular#29164
5f201f2
to
594072e
Compare
caretaker note: just to be safe, let's merge and sync this one on its own. I don't anticipate issues as a result of this fix, but you never know. |
…t cause an infinite loop (#36605) When loading a module that doesn't provide `RouterModule.forChild()` preloader will get stuck in an infinite loop and throw `ERROR Error: Maximum call stack size exceeded.` The issue is that child module's `Injector` will look to its parent `Injector` when it doesn't find any `ROUTES` so it will return routes for it's parent module instead. This will load the child again that returns its parent's routes and so on. Closes #29164 PR Close #36605
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: #29164
When loading a module that doesn't provide
RouterModule.forChild()
preloader will get stuck in a infinite loop and throwERROR Error: Maximum call stack size exceeded
.https://stackblitz.com/edit/angular-issue-repro2-atwzck?file=src%2Fapp%2Ffeature1%2Ffeature1.module.ts
The issue is that child module's
Injector
will look to its parentInjector
when it doesn't find anyROUTES
so it will return routes for it's parent module instead. This will load the child again that returns its parent's routes and so on.What is the new behavior?
Lazy loaded modules without
RouterModule.forChild()
won't break the app.I was thinking that this might throw an exception telling me that I'm lazy loading a module that doesn't define any routes but maybe it is a valid use-case (for example I'm loading a module that only does some logic in its constructor).
Does this PR introduce a breaking change?
Other information
I only changed one line and added one test that would fail with
Error: 1 timer(s) still in the queue.
without this change:https://github.com/angular/angular/compare/master...martinsik:issue-29164?expand=1#diff-2a29f955e9e56e86ee957a9768a19929R44
All other changes were made by running
yarn gulp format
Closes #29164