Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Provide the same service instance under different tokens

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?

like image 661
Alexander Abakumov Avatar asked Sep 21 '17 15:09

Alexander Abakumov


People also ask

What is Di token Angular?

The Dependency Injection system in Angular uses tokens to uniquely identify a Provider. There are three types of tokens that you can create in Angular. They are Type Token, String Token, and Injection Token. DI Tokens.

What is provide and useClass in Angular?

The provider-definition key can be one of the following: useClass - this option tells Angular DI to instantiate a provided class when a dependency is injected. useExisting - allows you to alias a token and reference any existing one. useFactory - allows you to define a function that constructs a dependency.

What is true about the useClass provider option?

Class Provider: useClassThe useClass expects us to provide a type. The Injector creates a new instance from the type and injects it. It is similar to calling the new operator and returning instance. If the type requires any constructor parameters, the injector will resolve that also.

What is useExisting in Angular?

The useExisting provider key lets you map one token to another. In effect, the first token is an alias for the service associated with the second token, creating two ways to access the same service object.


1 Answers

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

like image 198
Drenai Avatar answered Nov 16 '22 00:11

Drenai