Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doing Inversion of Control (IoC) in Angular

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?

like image 764
hzitoun Avatar asked May 16 '17 08:05

hzitoun


People also ask

Does angular use Inversion of Control?

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.

What do you mean by IoC Inversion of Control?

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.

What is Inversion of Control IoC and why we should use it in code?

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.


2 Answers

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) { 
  }
}
like image 120
hzitoun Avatar answered Sep 25 '22 20:09

hzitoun


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) {
  }
}
like image 22
Jonathan Pinzon Avatar answered Sep 25 '22 20:09

Jonathan Pinzon