Consider we have an interface called MapService
, and two implementations: GoogleMapsService
and LeafletMapService
. I want a package (or Angular2?) to call the needed implementation rather than developers.
export interface MapService {
//define my API
}
@Injectable()
export class GoogleMapsService implements MapService {
//implement the API
}
That means, in the component I want the type of the service to be the interface (so not depending on the implementation):
import { Component } from '@angular/core';
import { MapService } from './map.service';
import { GoogleMapsService } from './google-maps.service';
@Component({
template : `...`,
providers: [GoogleMapsService]
})
export class MyComponent {
constructor(private googleMapsService : MapService) { //notice the type of the service here
}
}
How can I achieve that?
This is termed as IOC(Inversion of Control) where control is being inverted to the external entity. Angular offers such a system that helps not only to register but also to instantiate component dependencies.
Inversion of control is a software design principle that asserts a program can benefit in terms of pluggability, testability, usability and loose coupling if the management of an application's flow is transferred to a different part of the application.
Inversion of Control (IoC) is a design principle that allows classes to be loosely coupled and, therefore, easier to test and maintain. IoC refers to transferring the control of objects and their dependencies from the main program to a container or framework.
So based on @jb-nizet's great comment; I've managed to do what I want using InjectionToken. Here is a code snippets:
import { Injectable, InjectionToken } from '@angular/core';
export const GOOGLE_MAPS_IMPL = new InjectionToken<MapService>('googleMapImpl');
export interface MapService {
//define the API
}
@Injectable()
export class GoogleMapsService implements MapService {
//implement the API
}
And the component:
import { Component, Inject } from '@angular/core';
import { MapService } from './map.service';
import { GoogleMapsService, GOOGLE_MAPS_IMPL } from './google-maps.service';
@Component({
template : `...`,
providers: [{ provide: GOOGLE_MAPS_IMPL, useClass: GoogleMapsService }]
})
export class MyComponent {
constructor(@Inject(GOOGLE_MAPS_IMPL) private googleMapsService : MapService) {
}
}
Another solution would be to use an abstract class instead of an interface.
export abstract class MapService{
// ...
}
make your service implements this abstract class and provide it
import { Component } from '@angular/core';
import { MapService } from './map.service';
import { GoogleMapsService } from './google-maps.service';
@Component({
template : `...`,
providers: [
{ provide: MapService, useClass: GoogleMapsService }
]
})
export class MyComponent {
constructor(private googleMapsService : MapService) {
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With