在 Angular 中进行控制反转 (IoC)

Doing Inversion of Control (IoC) in Angular

假设我们有一个名为 MapService 的接口,以及两个实现:GoogleMapsServiceLeafletMapService。我想要一个包(或 Angular2?)来调用所需的实现而不是开发人员。

export interface MapService {
//define my API
}

@Injectable()
export class GoogleMapsService implements MapService {
//implement the API
}

这意味着,在组件中我希望服务的类型是接口(因此不依赖于实现):

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
  }
}

我怎样才能做到这一点?

在提供程序数组中使用以下语法:

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) {
  }
}

请注意,您可以在模块范围内做同样的事情。

所以基于@jb-nizet 的精彩评论;我已经设法使用 InjectionToken 做我想做的事。这是一个代码片段:

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
}

和组件:

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) { 
  }
}

另一种解决方案是使用抽象 class 而不是接口。

  export abstract class MapService{
   // ...
  }

让你的服务实现这个抽象class 并提供

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) {
  }
}