Angular 6 service with interface
Solution 1
This can be done with InjectionToken
, which is a replacement for the obsolete OpaqueToken
export const AuthenticationProvider = new InjectionToken(
"AuthenticationProvider",
{ providedIn: "root", factory: () => new CognitoAuthenticationProvider() }
);
...
@Injectable()
export class CognitoAuthenticationProvider implements IAuthenticationProvider {
...
@Injectable({
providedIn: "root"
})
export class AuthenticationService {
constructor(
@Inject(AuthenticationProvider)
private authenticationProvider: IAuthenticationProvider,
private http: HttpClient
) {}
Solution 2
I used something like the following to solve this
app.module.ts
providers: [
{ provide: AlmostInterface, useClass: environment.concrete }
...
]
AlmostInterface.ts
export abstract class AlmostInterface {
abstract myMethod();
}
MyConcrete.ts
export class MyConcrete implements AlmostInterface {
myMethod() { ... }; // implementation
}
export class MyConcreteAlternative implements AlmostInterface {
myMethod() { ... }; // implementation
}
environment.ts
export const environment = {
production: false,
concrete: MyConcreteAlternative
};
environment.prod.ts
export const environment = {
production: true,
concrete: MyConcrete
};
Solution 3
I think you can't use typescript interfaces for dependency injection as typescript interfaces don't exist at runtime (only for typesafety at compile time).
I would suggest using an abstract class for it.
EDIT:
It seems you can use useClass
in the first parameter of @Injectable, not as a second like your example. Combining that with @k0zakinio's answer results in:
@Injectable({
providedIn: 'root',
useClass: environment.concrete,
deps: []
})
export abstract class SessionStorage { }
It also seems you need to declare your dependencies via deps
or inject
, checkout this github issue. I hope this time my answer is of more help.
Mr.wiseguy
Updated on July 05, 2022Comments
-
Mr.wiseguy almost 2 years
I am building an application with Angular (
6.0.7
) and I am trying to create a service with the new:@Injectable({ providedIn: 'root' })
But how can I type an injection with an Interface?
The problem
I have 2 services, Authentication.service and SessionStorage.service. I want to inject the sessionstorage into the authentication service. That can be done via:
constructor(private sessionStorage: SessionStorage) { }
No problem there. But for Object Orientated purposes I want to have an
interface
above this service (so that I can implement both localstorage service as sessionstorage service). Thus it is only logical that I want to type the injected class with the interface, but this cannot be done the same way Angular 5 and lower does it.So how can I type the injection into this global service with my interface?
I've tried
The Angular service typings describe an
InjectableProvider
, but this does not match any of the parameters of the siblings ofInjectableProvider
, so this gives a compiler (and tslint) error.@Injectable({ providedIn: 'root' }, {provide: IStorageService, useClass: SessionStorage})
-
Mr.wiseguy almost 6 yearsThis does not match the params defined by the
InjectableProvider
, so unfortunately this does not work -
Mr.wiseguy almost 6 yearsThis works, but the problem is that this uses the old Angular 5 syntax for declaring a service. I am asking for a solution with the new Angular 6 syntax, since this (
provideIn: 'root'
) is not declared via the module, but is only injected in the components that use it (better for tree shaking). -
Mr.wiseguy almost 6 yearsWell, Angular (and Typescript) does provide a way for dependency injection, see @k0zakinio's answer. So it is possible. But I want to know if it's also possible for the Angular 6 way of services.
-
SirDieter almost 6 yearsSorry, I totally misunderstood your question.
-
Mr.wiseguy over 5 yearsI think this is the best option, unfortunately it needs yet an other variable and the
provideIn
needs to be defined on the token, where I would expect it to be defined on the service. -
jenson-button-event over 5 years@Mr.wiseguy the
providedIn
is new in ng 6 and is recommended for some tree-shaking optimisation purpose. you can still register providers in the modules themselves. -
Mr.wiseguy over 5 yearsI agree with you on that (I really like the new
providedIn
option), but what I ment was that you can no longer decalre this on the service itself. It has to be declared on the injection token, otherwise it will not be available in the whole application. -
tsiro over 5 yearswhat if we would like to inject something into 'CognitoAuthenticationProvider' constructor?
-
MegaCasper over 5 yearsI pay attention on the interface
IAuthenticationProvider
name. Angular style guide says: Consider naming an interface without an I prefix. -
crush about 4 years@tsiro I had the same question. Angular docs state the following about that:
When creating an InjectionToken, you can optionally specify a factory function which returns (possibly by creating) a default value of the parameterized type T. This sets up the InjectionToken using this factory as a provider as if it was defined explicitly in the application's root injector. If the factory function, which takes zero arguments, needs to inject dependencies, it can do so using the inject function.
They provide an example. -
crush about 4 years@tsiro Also keep in mind the
factory
parameter is optional, and only provides a default for the injection token. You might be in a case where you don't have a default to provide, in which case you shouldn't use thefactory
parameter at all. This would let angular fail with an error if you hadn't configured the injector to have any services to inject with that token elsewhere. You can setup the provider when adding your service to a module to useproviders: [{ provide: YOUR_TOKEN, useClass: YourConcreteClass }]