Dependency injection 如何使用依赖项注入扩展组件?

Dependency injection 如何使用依赖项注入扩展组件?,dependency-injection,angular,Dependency Injection,Angular,我有以下类模块,它包含http: @Injectable() 导出默认类{ 构造函数(私有fetchApi:fetchApi){} } 我想使用它如下: @组件({ 选择器:'main', 提供者:[获取API] }) 导出默认类扩展ModuleWithHttp{ onInit(){ this.fetchApi.call(); } } 因此,通过扩展一个已经注入依赖项的超类,我希望能够在其子类中访问它 我尝试了许多不同的方法,甚至将super class作为组件: @组件({ 提供者:[获取

我有以下类
模块,它包含http

@Injectable()
导出默认类{
构造函数(私有fetchApi:fetchApi){}
}
我想使用它如下:

@组件({
选择器:'main',
提供者:[获取API]
})
导出默认类扩展ModuleWithHttp{
onInit(){
this.fetchApi.call();
}
}
因此,通过扩展一个已经注入依赖项的超类,我希望能够在其子类中访问它

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

@组件({
提供者:[获取API]
})
导出默认类{
构造函数(私有fetchApi:fetchApi){}
}

但是,即使在超类中,
this.fetchApi
也是
null

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

导出默认类扩展了ModuleWithHttp{
构造函数(fetchApi:fetchApi){
super(fetchApi);
}   
onInit(){
this.fetchApi.call();
}
}
这是DI一般工作方式的一个特点。超类将通过继承实例化子类,但您必须向子类提供所需的参数

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

编辑:从Angular 5.0.0开始,ReflectiveInjector已被弃用,取而代之的是StaticInjector,下面是反映此更改的更新代码

与deps建立服务地图

export const services:{[key:string]:{provide:any,deps:any[],useClass?:any}={
“FetchApi”:{
提供:FetchApi,
副部长:[]
}
}
有一个喷油器支架

从“@angular/core”导入{Injector}”;
导出类ServiceLocator{
静态喷油器:喷油器;
}
在AppModule中设置它

@NgModule(…)
导出类AppModule{
构造函数(){
ServiceLocator.injector=injector.create(
Object.keys(services.map)(key=>({
提供:服务[key]。提供,
useClass:services[key]。提供,
deps:服务[key]。deps
}))
);
}
}
使用父类中的喷油器

导出类父类{
受保护的fetchApi:fetchApi;
构造函数(){
this.fetchApi=ServiceLocator.injector.get(fetchApi);
....
}
}
并扩展父类,这样就不需要注入
FetchApi
服务

导出类ChildClass扩展ParentClass{
构造函数(){
超级();
...
}
onInit(){
this.fetchApi.call();
}
}

可能响应延迟,但我已决定只在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);
}

请注意,MyService1和MyService2是由MyBaseClass创建的新实例

这也是我读到的内容。我想抽象DI以避免整个样板文件,但我发现我需要以几乎相同的方式导入、注入和扩展子类。我不明白这一点。“基类”是指“派生类”(通常“基类”与“超类”相同)吗?在您的示例中,
fetchAPI
被“传递”到哪里?你是说,如果我构造一个新的派生类,超类中所要求的注入将被实现吗?“这是DI通常如何工作的一个特性”-通常在角度上,但不是在所有地方。许多框架通过属性绑定支持DI。我在回答这个问题时遇到了一个问题:向超类添加新的注入服务将破坏所有子类,除非您使用相同的新参数更新每个构造函数中的super()调用。如果您正在开发一个包库,并且不一定能够访问从您自己继承的类,那么这并不理想。不确定是否有办法解决这个问题,但我愿意接受建议!您需要将FetchApi注入继承类,但如果有帮助,您可以从基类访问它,执行类似于
(this).FetchApi.call(…)
(您不需要通过
super()显式传递它)
。我如何从child注入服务,并从parent使用它呢?我和Petr在一起-据我所知,父类根本不知道它们的子类中有什么。这就像《大白鲨》中的罗伊·谢德(Roy Scheider)的角色知道《大白鲨》中迈克尔·凯恩的遭遇:复仇。我坚持认为这是一个完美的类比。很棒的方法我的问题。根据angular 2.3的这一特性,我认为我可以将Java的所有程序模式迁移到angular,以减少大量重复代码!我喜欢这个解决方案,它工作起来很有魅力。不过,我还没有用不同的产品版本对它进行过测试。这能与树震动一起工作吗?@PetrMarek看起来像是由它注入的服务解决方案创建实例不同于通常的构造函数注入。这是真的吗?这不是一个大问题,但如果我只想将此解决方案用于继承,是否有办法使用构造函数中的同一实例?实际上,您甚至可以避免注入器,这足以让它超出您的基础class@Sampgun怎么做?谢谢y干净的解决方案!(y)@Sampgun他说的是将其作为在模块根中定义的静态单例。这里有一种方法可以做到这一点@Bon。全局是一种很酷的干净方法来实现这一点
constructor( private injector: Injector) {

   super(injector);

}
export class MyBaseClass {

  injector = Injector.create({
    providers: [
      { provide: MyService1, deps: [] },
      { provide: MyService2, deps: [] }
    ]
  });

  constructor() {
    this.myService1 = this.injector.get(MyService1);
    this.myService2 = this.injector.get(MyService2);
  }
}

export class MyChildClass extends MyBaseClass {
  constructor() {
    super();
    // this.myService1 is accessible
    // this.myService2 is accessible
  }
}