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 LazyModuleroutes to aLazyComponentwhich injects two services:HelloServiceandGoodbyeServiceHelloServiceisprovidedIn: 'root'GoodbyeServiceisprovidedIn: LazyModule- Click the “Lazy” link to route to the LazyComponent
HelloServiceis successfully instantiatedGoodbyeServicefails 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 doin 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
GoodbyeServiceon 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
GoodbyeServiceviaLazyModule’sprovidersisn’t deprecated, is it? <del>So ifLazyComponentdepends onGoodbyeServicethenGoodbyeServicedoesn’t fall off the tree, anyways. BecauseLazyComponentis used within the module’sdeclarationarray and as suchGoodbyeServiceis 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
providedInthen 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.