angular: Treeshakeable Injector resolution logic doesn't work in a lazy loaded scenario
I’m submitting a…
[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:
Current behavior
When combining lazy-loading via the Router + the v6 providedIn: SomeModule
syntax, the injector resolution step seems to fail.
Expected behavior
Should not throw an error
Minimal reproduction of the problem with instructions
See https://stackblitz.com/edit/angular-v6-providedin-4gxxoa?file=app/lazy.component.ts
Observe that:
- AppModule uses the Router to lazy-load the
LazyModule
LazyModule
routes to aLazyComponent
which injects two services:HelloService
andGoodbyeService
HelloService
isprovidedIn: 'root'
GoodbyeService
isprovidedIn: LazyModule
- Click the “Lazy” link to route to the LazyComponent
HelloService
is successfully instantiatedGoodbyeService
fails withERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[LazyComponent -> GoodbyeService]:
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 11
- Comments: 17 (15 by maintainers)
I see what you mean.
IMHO the new API is a nice thing b/c it makes default cases easier and allows for tree shaking. I also like the fact that I don’t need to update the module anymore. For more advanced use cases, we need to align with some patterns. They are not difficult, but one needs to know them.
I’ve written some things up, I ran into. Perhaps this is useful for you:
Blogged: The new Treeshakable Providers API in @Angular: Why, How And Cycles
http://www.softwarearchitekt.at/post/2018/05/06/the-new-treeshakable-providers-api-in-angular-why-how-and-cycles.aspx
@wKoza … ah, of course, you are right. 😄 I meant
nothing to do
in a sense of code that would allow to use syntax likeprovidedIn: LazyModule
.As @manfredsteyer says, this is the result of a circular dependency between the component, service, and module.
Injector_
is I believe the node injector (for components, directives, etc). It shouldn’t resolve lazy providers, as you can’t sayprovidedIn: SomeComponent
. I’ll have to look why this doesn’t work with lazy loading, it’s likely a bug.… but as I guess it’s not intended to instantiate
GoodbyeService
on app’s root and also it’s required not to useproviders: []
section ofLazyModule
.Yes @manfredsteyer , you can also put the GoodbyeService in the root module with
providedIn: 'root'
. GoodbyeService will be well in the chunk of the layModuleI ran into this too. The (one) reson for this seems to be that such a scenario causes a cycle:
I think perhaps the doc should contain an example for this situation @alxhub
@manfredsteyer Is the promise of tree-shakability worth the complexity at all? The old way of providing
GoodbyeService
viaLazyModule
’sproviders
isn’t deprecated, is it? <del>So ifLazyComponent
depends onGoodbyeService
thenGoodbyeService
doesn’t fall off the tree, anyways. BecauseLazyComponent
is used within the module’sdeclaration
array and as suchGoodbyeService
is transitively used when injected intoLazyComponent
.</del> Edit Well it does fall off when only used at a type position in LazyComponent. Found my mistake.In parts I see my concerns proved in how people try to systematically migrate their existing code base to tree-shakable providers using
providedIn
then running into issues with circular deps. I think the topic deserves an Appendix in the Dependency Injection docs.Citing the docs
Citing @hansl
I’ve discussed this with @alxhub the other day. We can resolve the cycle by putting the service in an own service module:
Just tried it out. Works like a charm.