如何在Angular 2中扩展具有依赖注入的组件?

Kam*_*nek 35 dependency-injection angular

我有以下课程ModuleWithHttp:

@Injectable()
export default class {
  constructor(private fetchApi: FetchApi) {}
}
Run Code Online (Sandbox Code Playgroud)

我想用它如下:

@Component({
  selector: 'main',
  providers: [FetchApi]
})
export default class extends ModuleWithHttp {
  onInit() {
    this.fetchApi.call();
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,通过扩展已经注入依赖项的超类,我想在其子代中访问它.

我尝试了许多不同的方法,甚至将超类作为组件:

@Component({
  providers: [FetchApi]
})
export default class {
  constructor(private fetchApi: FetchApi) {}
}
Run Code Online (Sandbox Code Playgroud)

但是,即使是在超级阶层,this.fetchApi也是null如此.

Pet*_*rek 69

如果你想避免这种"锅炉板"代码在子类中注入服务只是为了在父类的构造函数中注入,然后通过继承在子类中有效地使用那些服务,你可以这样做:

编辑:来自Angular 5.0.0 ReflectiveInjector已被弃用,有利于StaticInjector,下面是更新代码以反映此更改

有deps的服务地图,

export const services: {[key: string]: {provide: any, deps: any[], useClass?: any}} = {
  'FetchApi': {
    provide: FetchApi,
    deps: []
  }
}
Run Code Online (Sandbox Code Playgroud)

有一个注射器支架,

import {Injector} from "@angular/core";

export class ServiceLocator {
  static injector: Injector;
}
Run Code Online (Sandbox Code Playgroud)

在AppModule中设置它,

@NgModule(...)
export class AppModule {
  constructor() {
    ServiceLocator.injector = Injector.create(
      Object.keys(services).map(key => ({
        provide: services[key].provide,
        useClass: services[key].provide,
        deps: services[key].deps
      }))
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在父类中使用注入器,

export class ParentClass {

  protected fetchApi: FetchApi;

  constructor() {
    this.fetchApi = ServiceLocator.injector.get(FetchApi);
    ....
  }
}
Run Code Online (Sandbox Code Playgroud)

并扩展父类,因此您不需要注入FetchApi服务.

export class ChildClass extends ParentClass {
  constructor() {
    super();
    ...
  }

  onInit() {
    this.fetchApi.call();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我和Petr在一起 - 据我所知,父类在他们的子类中根本不知道任何东西.这就像是来自Jaws的Roy Scheider的角色,知道在Jaws:The Revenge中Michael Caine会发生什么.我坚持认为这是一个完美的类比. (7认同)
  • @PetrMarek 似乎此解决方案注入的服务创建的实例不同于通常的构造函数注入。确实如此?这不是一个大问题,但如果我只想将此解决方案用于继承,有没有办法像往常一样从构造函数使用相同的实例? (2认同)

TGH*_*TGH 39

您必须在超类中注入fetchAPI并将其传递给子类

export default class extends ModuleWithHttp {

  constructor(fetchApi: FetchApi) {
     super(fetchApi);
  }   

  onInit() {
    this.fetchApi.call();
  }
}
Run Code Online (Sandbox Code Playgroud)

这是DI一般工作方式的一个特征.超类将通过继承实例化子进程,但您必须向子进程提供所需的参数.

  • 我遇到了这个问题的一个问题:向超类添加一个新的注入服务将破坏所有子类,除非你使用相同的新参数更新每个构造函数中的super()调用.如果您正在开发包库并且不一定能够访问从您自己继承的类,那么这并不理想.不确定是否有解决方法,但我愿意接受建议! (6认同)
  • _"这是DI一般工作方式的一个特征"_ - 通常是角度的,但一般不在任何地方.许多框架通过属性绑定支持DI. (5认同)

小智 24

也许是迟到的响应,但我已经解决了只注入BaseComponent和所有子类中的注入器,这样我就不需要维护服务定位器了.

我的基类:

constructor(private injectorObj: Injector) {

  this.store = <Store<AppState>>this.injectorObj.get(Store);
  this.apiService = this.injectorObj.get(JsonApiService);
  this.dialogService = this.injectorObj.get(DialogService);
  this.viewContainerRef = this.injectorObj.get(ViewContainerRef);
  this.router = this.injectorObj.get(Router);
  this.translate = this.injectorObj.get(TranslateService);
  this.globalConstantsService = this.injectorObj.get(GlobalConstantsService);
  this.dialog = this.injectorObj.get(MatDialog);
  this.activatedRoute = this.injectorObj.get(ActivatedRoute);
}
Run Code Online (Sandbox Code Playgroud)

我的孩子班:

constructor( private injector: Injector) {

   super(injector);

}
Run Code Online (Sandbox Code Playgroud)