继承和依赖注入

max*_*xhb 76 inheritance inject typescript typescript1.8 angular

我有一组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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Gün*_*uer 60

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

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

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

绕过注射器并获得依赖性势在必行具有严重的缺点.

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

  • 他自己的问题的答案是一个丑陋的黑客.这个问题已经证明了应该如何做.我详细说明了一下. (8认同)
  • 这个答案是对的.OP回答了他们自己的问题,但在这样做时打破了很多惯例.你列出了实际的缺点也是有帮助的,我会保证它 - 我在想同样的事情. (7认同)
  • 我真的想(并继续)使用这个答案而不是OP的"黑客".但我不得不说,这似乎与DRY相差甚远,当我想在基类中添加依赖项时非常痛苦.我只需要向大约20多个类添加ctor注入(以及相应的`super`调用),这个数字将来只会增长.所以有两件事:1)我讨厌看到"大代码库"这样做; 2)感谢上帝为vim`q`和vscode`ctrl + .` (4认同)
  • 仅仅因为它不方便并不意味着它是不好的做法.构造函数很不方便,因为很难可靠地完成对象初始化.我认为更糟糕的做法是构建一个需要"基类注入15个服务并且由6个继承"的服务. (3认同)
  • 为了清楚起见:我在任何地方都需要它。尝试将该依赖关系移至我的超类,以便EACH派生类可以访问服务,而无需将其分别注入每个派生类。 (2认同)
  • @ibgib我知道的所有语言都是一样的.如果您想对行为做出保证,那么构造函数很难. (2认同)
  • 我不明白为什么人们如此沮丧以至于他们不得不传入注入的服务。这个答案是正确的,即使其他人很可爱 - 这就像在其他语言中一样。 (2认同)
  • @ktamlyn 我也是这么看的。我的印象是这取决于你来自哪里。如果你来自 Java 或类似的东西,这就是世界的运作方式,如果你来自 JS,即使导入也被认为是大量的样板,而构造函数是 Illuminaty 试图让每个开发人员发疯;-) (2认同)

max*_*xhb 54

更新的解决方案,通过使用全局注入器来防止生成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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • @Gunter Zochbauer的回答是正确的.这不是正确的方法,并打破了很多角度约定.编码所有这些注入调用可能更简单,但是如果你想牺牲必须编写构造函数代码以便能够维护一个大的代码库,那么你就是在自己的脚下.这个解决方案不具有可扩展性,IMO,并将导致许多令人困惑的错误. (8认同)
  • 这使得很难理解实际注入了哪些服务. (7认同)
  • 不存在同一服务的多个实例的风险.您只需在应用程序的根目录中提供服务,以防止可能在应用程序的不同分支上发生的多个实例.将服务传递给继承更改*不会*创建类的新实例.@Gunter Zochbauer的回答是正确的. (3认同)

Leu*_*ipp 6

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

派生类:

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

基类:

export class BaseComponent {
    constructor(protected providerService: ProviderService) {
        // do something with providerService
    }
}
Run Code Online (Sandbox Code Playgroud)

提供服务类:

@Injectable()
export class ProviderService {
    constructor(private _apiService: ApiService, private _authService: AuthService) {
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这里的问题是您可能会创建一个“垃圾抽屉”服务,它本质上只是 Injector 服务的代理。 (3认同)

Mat*_*hel 6

据我所知,现在可以使用inject() https://angular.io/api/core/inject以非常简单的方式使用 Angular v14 实现这一点,因为您现在可以在构造函数外部的“字段初始值设定项”中设置依赖项完全。

我使用了一些简单的令牌依赖项DOCUMENT作为LOCALE_ID示例。

import { DOCUMENT } from '@angular/common';
import { inject } from '@angular/core';

export abstract class AbstractComponent {
  abstractDependency = inject(DOCUMENT);
}
Run Code Online (Sandbox Code Playgroud)
import { Component, inject, LOCALE_ID } from '@angular/core';
import { AbstractComponent } from './abstract.component';

@Component({
  selector: 'my-app',
  template: '{{ abstractDependency }} {{ myDependency }}',
})
export class MyComponent extends AbstractComponent {
  myDependency = inject(LOCALE_ID);
}
Run Code Online (Sandbox Code Playgroud)

实例:https://stackblitz.com/edit/angular-ivy-zvlx1d ?file=src/app/my.component.ts

  • 正是如此!我想知道我是否可以注入省略构造函数:) (3认同)