继承和依赖注入

时间:2016-08-19 12:11:55

标签: inheritance angular typescript inject typescript1.8

我有一组angular2组件,它们都应该注入一些服务。我的第一个想法是,最好创建一个超级类并在那里注入服务。然后我的任何组件都会扩展该超类,但这种方法不起作用。

简化示例:

export class AbstractComponent {
  constructor(private myservice: MyService) {
    // Inject the service I need for all components
  }
}

export MyComponent extends AbstractComponent {
  constructor(private anotherService: AnotherService) {
    super(); // This gives an error as super constructor needs an argument
  }
}

我可以通过在每个组件中注入MyService并使用该参数进行super()调用来解决这个问题,但这肯定是某种荒谬的。

如何正确组织我的组件,以便他们从超类继承服务?

7 个答案:

答案 0 :(得分:55)

  

我可以通过在每个组件中注入MyService并将该参数用于super()调用来解决这个问题,但这肯定是某种荒谬的。

这并不荒谬。这就是构造函数和构造函数注入的工作原理。

每个可注入类都必须将依赖项声明为构造函数参数,如果超类也具有依赖项,则需要在子类中列出这些依赖项。也是构造函数,并通过super(dep1, dep2)调用传递给超类。

绕过注射器和获取依赖关系必然会有严重的缺点。

它隐藏了使代码更难阅读的依赖关系 它违反了一个熟悉Angular2 DI如何工作的人的期望 它打破了离线编译,生成静态代码以替换声明性和命令式DI,以提高性能并减少代码大小。

答案 1 :(得分:51)

更新的解决方案,通过使用全局注入器来阻止生成myService的多个实例。

import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class AbstractComponent {
  protected myServiceA:MyServiceA;
  protected myServiceB:MyServiceB;
  protected myServiceC:MyServiceC;

  constructor(injector: Injector) {
    this.settingsServiceA = injector.get(MyServiceA);
    this.settingsServiceB = injector.get(MyServiceB);
    this.settingsServiceB = injector.get(MyServiceC);
  }
}

export MyComponent extends AbstractComponent {
  constructor(
    private anotherService: AnotherService,
    injector: Injector
  ) {
    super(injector);

    this.myServiceA.JustCallSomeMethod();
    this.myServiceB.JustCallAnotherMethod();
    this.myServiceC.JustOneMoreMethod();
  }
}

这将确保MyService可以在任何扩展AbstractComponent的类中使用,而无需在每个派生类中注入MyService。

此解决方案有一些缺点(请参阅我原始问题下面的@GünterZöchbauer的Ccomment):

  • 当有许多不同的服务需要在许多地方注入时,注入全局注入器只是一种改进。如果您只有一个共享服务,那么在派生类中注入该服务可能更好/更容易
  • 我的解决方案和他提议的替代方案都有缺点,它们使得更难以确定哪个类取决于什么服务。

有关Angular2中依赖注入的书面解释,请参阅此博客文章,这有助于我解决问题:http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html

答案 2 :(得分:3)

我没有手动注入所有服务,而是创建了一个提供服务的类,例如,它获取了注入的服务。然后将此类注入派生类并传递给基类。

派生类:

@Component({
    ...
    providers: [ProviderService]
})
export class DerivedComponent extends BaseComponent {
    constructor(protected providerService: ProviderService) {
        super(providerService);
    }
}

基类:

export class BaseComponent {
    constructor(protected providerService: ProviderService) {
        // do something with providerService
    }
}

提供服务的课程:

@Injectable()
export class ProviderService {
    constructor(private _apiService: ApiService, private _authService: AuthService) {
    }
}

答案 3 :(得分:1)

而不是注入一个包含所有其他服务作为依赖项的服务,如下所示:

class ProviderService {
    constructor(private service1: Service1, private service2: Service2) {}
}

class BaseComponent {
    constructor(protected providerService: ProviderService) {}

    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

我会跳过这个额外的步骤,只需在BaseComponent中添加注入所有服务,如下所示:

class BaseComponent {
    constructor(protected service1: Service1, protected service2: Service2) {}
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        this.service1;
        this.service2;
    }
}

这种技术假设有两件事:

  1. 您的担忧与组件继承完全相关。最有可能的是,你在这个问题上的原因是因为你需要在每个派生类中重复使用大量的非干(WET?)代码。如果您希望所有组件和服务的单个入口点受益,则需要执行额外步骤。

  2. 每个组件都会扩展BaseComponent

  3. 如果您决定使用派生类的构造函数,则还有一个缺点,因为您需要调用super()并传入所有依赖项。虽然我没有真正看到需要使用constructor代替ngOnInit的用例,但完全有可能存在这样的用例。

答案 4 :(得分:0)

如果已从第三方插件获取父类(并且您无法更改源),则可以执行此操作:

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

export MyComponent extends AbstractComponent {
  constructor(
    protected injector: Injector,
    private anotherService: AnotherService
  ) {
    super(injector.get(MyService));
  }
}

或更好的方法(在构造函数中只保留一个参数):

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

export MyComponent extends AbstractComponent {
  private anotherService: AnotherService;

  constructor(
    protected injector: Injector
  ) {
    super(injector.get(MyService));
    this.anotherService = injector.get(AnotherService);
  }
}

答案 5 :(得分:0)

据我了解,为了从基类继承,您首先需要实例化它。为了实例化它,您需要传递其构造函数必需的参数,这样就可以通过super()调用将它们从子级传递到父级,这样才有意义。当然,喷油器是另一种可行的解决方案。

答案 6 :(得分:0)

丑陋的人

前段时间,我的一些客户希望将两个BIG角度项目加入到昨天(角度v4到角度v8)。 Project v4对每个组件使用BaseView类,并且包含tr(key)的翻译方法(在v8中,我使用ng-translate)。因此,为了避免切换翻译系统并并行编辑数百个模板(在v4中)或设置2翻译系统,我使用了以下丑陋的技巧(我对此并不感到骄傲)-在AppModule类中,我添加了以下构造函数:< / p>

export class AppModule { 
    constructor(private injector: Injector) {
        window['UglyHackInjector'] = this.injector;
    }
}

现在AbstractComponent就可以使用

export class AbstractComponent {
  private myservice: MyService = null;

  constructor() {
    this.myservice = window['UglyHackInjector'].get(MyService);
  }
}