Reputation: 14579
I have 2 interfaces:
export interface IUserService {
...
}
export interface IUserStorageService {
...
}
And a single service implementing both of them:
@Injectable()
export class UserService implements IUserService, IUserStorageService {
...
}
(This part seems to be irrelevant for the question, so it's just for the sake of completeness. I can easily convert interfaces to abstract classes to use them directly as tokens without additional injection tokens.)
Now, since Angular doesn't support interfaces as tokens for providers, I have to create injection tokens:
export let USER_SERVICE: InjectionToken<IUserService> = new InjectionToken<IUserService>("user.service");
export let USER_STORAGE_SERVICE: InjectionToken<IUserStorageService> = new InjectionToken<IUserStorageService>("user-storage.service");
And now I'm able to map those injection tokens to the single service class globally in my app.module.ts
:
@NgModule({
...
providers: [
{ provide: USER_SERVICE, useClass: UserService },
{ provide: USER_STORAGE_SERVICE, useClass: UserService }
],
...
})
export class AppModule {
...
}
And finally, I'm now able to inject the service under different interfaces to my components:
// Some component - sees service's API as IUserService
constructor(@Inject(USER_SERVICE) private readonly userService: IUserService) {
}
// Another component- sees service's API as IUserStorageService
constructor(@Inject(USER_STORAGE_SERVICE) private readonly userStorageService: IUserStorageService) {
}
The issue here is that Angular actually creates 2 instances of UserService
, one for the each token, while I need UserService
to be a single instance per app.
How can I achieve that?
Upvotes: 14
Views: 2848
Reputation: 12407
I had a very similar requirement: I was providing a service to a number of components - the service has a dispatchEvent
function and a subscribeToEvents
function. I want it to be clear that only the managing component can use dispatchEvent()
, so the service implements
two abstract classes (these can then be used as tokens - which is clearer than the injection token format) - one has dispatchEvents, the other has subscribeToEvents
providers: [
{provide: AbstractSettingsManager, useClass: SettingsEventService},
{provide: AbstractSettingsConsumer, useExisting: AbstractSettingsManager}
],
You can use the useExisting
key, and keep in mind that it means "use the existing token"
The components will all have the same ServiceEventService
instance, but as I'm providing the AbstractSettingsManager
/AbstractSettingsConsumer
the components will only have access (via TypeScript checks) to one or other of the services functions
Upvotes: 16