- Notifications
You must be signed in to change notification settings - Fork27k
Description
Which @angular/* package(s) are relevant/related to the feature request?
core
Description
Currently providers can be provided only in component or module or root or etc.. but not inside other providers.
- One issue is that one cannot create a provider which depends upon another provider privately.
- Another issue is multiple instances of the same provider which are linked to the provider scope it is provided in.
An example of a case that currently suffer from those problems:
@Injectable()classCounterServiceimplementsOnDestroy{ngOnDestroy():void{}}constprimaryCounterToken=newInjectionToken<CounterService>('primaryCounterToken');constprimaryCounterProvider:Provider={provide:primaryCounterToken,useClass:CounterService,};constsecondaryCounterToken=newInjectionToken<CounterService>('secondaryCounterToken');constsecondaryCounterProvider:Provider={provide:secondaryCounterToken,useClass:CounterService,};@Injectable()classTwoCountersService{constructor( @Inject(primaryCounterToken)primary:CounterService, @Inject(secondaryCounterToken)secondary:CounterService){}}consttwoCountersProviders:Provider[]=[primaryCounterProvider,secondaryCounterProvider,TwoCountersService,];
In this caseCounterService has some logic but inTwoCountersService two instances of it are required. The solution here is usingInjectionToken to create two instances and inject them. The second issue mentioned above is yet to be seen in this example, but the first issue is.
Lets assume I want to use thisTwoCountersService in a component - I need to provide all three tokens in the component providers - hence thetwoCountersProviders array. Ideally I would like to just specifyTwoCountersService.
Now lets assume I want two instances ofTwoCountersService just like I wanted withCounterService. At this point I am stuck due to the second issue - as I actually need 4 instances ofCounterService which is not possible this way.
The only solution is using a factory provider:
constcounterFactoryToken=newInjectionToken<CounterService>('counterFactoryToken');constcounterFactoryProvider:Provider={provide:secondaryCounterToken,useFactory:()=>()=>newCounterService(),};@Injectable()classTwoCountersService{privateprimary=this.counterFactory();privatesecondary=this.counterFactory();constructor( @Inject(counterFactoryToken)privatecounterFactory:()=>CounterService){}}// Two instances:constfirstTwoCountersToken=newInjectionToken<TwoCountersService>('firstTwoCountersToken');constfirstTwoCountersProvider:Provider={provide:firstTwoCountersToken,useClass:TwoCountersService,};constsecondTwoCountersToken=newInjectionToken<TwoCountersService>('secondTwoCountersToken');constsecondTwoCountersProvider:Provider={provide:secondTwoCountersToken,useClass:TwoCountersService,};
Now i can safely injectfirstTwoCountersToken andsecondTwoCountersToken and have two instances and get 4 new instances ofCounterService.
The first issue is even more apparent here - I need to provide now:counterFactoryProvider,firstTwoCountersProvider andsecondTwoCountersProvider and if there was a service injecting both - that service too.
The second issue is solved here.
But - I lose lifecycle methods (ngOnDestroy) this way which isn't what I wanted.
So instead of factory provider I need a service and manually handle lifecycle... something like:
@Injectable()exportclassCounterServiceFactory{privateregistry:CounterService[]=[];getNew(){constsrv=newCounterService();this.registry.push(srv);returnsrv;}ngOnDestroy():void{for(constsrvofthis.registry){srv.ngOnDestroy();}this.registry=[];}}
And I got it working withA LOT of code and roundabout issues to solve. It still requires annoyingly providing providers carefully and a factory service for every service.
Proposed solution
If it was possible to provide providers inside@Injectable - it could be solved nicely:
@Injectable()classCounterServiceimplementsOnDestroy{ngOnDestroy():void{}}constprimaryCounterToken=newInjectionToken<CounterService>('primaryCounterToken');constprimaryCounterProvider:Provider={provide:primaryCounterToken,useClass:CounterService,};constsecondaryCounterToken=newInjectionToken<CounterService>('secondaryCounterToken');constsecondaryCounterProvider:Provider={provide:secondaryCounterToken,useClass:CounterService,};@Injectable({providers:[primaryCounterProvider,secondaryCounterProvider]})classTwoCountersService{constructor( @Inject(primaryCounterToken)primary:CounterService, @Inject(secondaryCounterToken)secondary:CounterService){}}constfirstTwoCountersToken=newInjectionToken<TwoCountersService>('firstTwoCountersToken');constfirstTwoCountersProvider:Provider={provide:firstTwoCountersToken,useClass:TwoCountersService,};constsecondTwoCountersToken=newInjectionToken<TwoCountersService>('secondTwoCountersToken');constsecondTwoCountersProvider:Provider={provide:secondTwoCountersToken,useClass:TwoCountersService,};
Now if I needfirstTwoCountersProvider andsecondTwoCountersProvider - that's only what I provide and inject and all the different instances are handled automatically as providers provided in@Injectable are sharing the providing scope.
Even better if there was a service injecting both - I would just provide that service ONLY and inject it.
Alternatives considered
The long and complicated solution from above.