【问题标题】:Inject an Interface with Angular 4使用 Angular 4 注入接口
【发布时间】:2017-12-20 10:31:11
【问题描述】:

我正在尝试创建一个通用的DeleteableConfirmationComponent,它允许我显示一个确认对话框并从任何实现Deleteable 接口的注入服务调用delete 方法。

为此,我创建了这个接口:

export interface Deleteable {
  delete(object);
}

我有一个实现它的服务:

@Injectable()
export class LocalityService implements Deleteable {
  delete(locality): Observable<Locality> {
    // Delete logic.
  }
}

对于DeleteableConfirmationComponent,我尝试使用构造函数注入服务:

export class DeleteableConfirmationComponent {
  constructor(
    public dialog: MdDialogRef<DeleteableConfirmationComponent>,
    @Inject(MD_DIALOG_DATA) public data: any,
    private service: Deleteable
  ) {}

  delete() {
    this.service.delete(this.object)
                .subscribe(() => {
                  this.dialog.close();
                });
  }
}

但不幸的是,我收到一条错误消息,提示 它无法解析 DeleteableConfirmationComponent 的所有参数

目前,我正在使用对话框数据选项来传递我的服务:

confirmDelete(locality) {
  this.dialog.open(DeleteableConfirmationComponent, {
    data: {
      service: this.localityService
    }
  });
}

但它感觉很脏,并且确实允许注入任何类型的服务,而我想强制实现 Deleteable 接口的服务。

我想我可能会更好地使用 abstract class,但我更喜欢组合而不是继承。

有什么想法或最佳实践建议吗?

【问题讨论】:

  • Unfortunately, you cannot use a TypeScript interface as a tokenangular.io/guide/…
  • 抽象类是这里的最佳选择
  • 抽象类的使用与组合与继承无关。类也可以用作接口。不一定要延长。

标签: angular typescript generics interface


【解决方案1】:

如 cmets 中所述,您可以将接口转换为抽象类:

export abstract class Deleteable {
  abstract delete(object);
}

然后在您的提供者中,您可以将其映射到真正的类:

providers: [{ provide: Deleteable, useValue: new LocalityService() }]

您可能不喜欢这种方法,因为现在看来LocalityService 必须扩展Deleteable。但是如果LocalityService 需要扩展其他类怎么办?不允许多重继承:

// Error: Classes can only extend a single class
export class LocalityService extends OtherClass, Deleteable { }

或者您可能根本不喜欢 Deleteable 现在出现在 LocalityService 的原型链中的事实:

export class LocalityService extends Deleteable {
  delete(locality): void {
    // Returns true
    alert(this instanceof Deleteable);
  }
}

但是,如this answer 所示,TypeScript 允许您将类视为接口。因此,您可以将implements 与抽象类一起使用。

export class LocalityService extends OtherClass implements Deleteable {
  delete(locality): void {
    // Returns false
    alert(this instanceof Deleteable);
  }
}

因此,就所有意图和目的而言,您的抽象类现在表现得像一个接口。它甚至不会出现在原型链中。

【讨论】:

  • 哇,我根本不知道 TypeScript 允许我将类视为接口。我将尝试其中的一些方法,并让你们知道。谢谢
  • @lkartono 我也不知道 - 我正要指导您使用此处所示的令牌:angular.io/guide/dependency-injection#injectiontoken。但是经过一些实验和研究(见这个答案:stackoverflow.com/questions/35990538/…)我发现抽象类更干净。
  • 如果我希望从父组件中检索提供者(使用值)怎么办?
  • @snezed 我不确定我是否理解 - 似乎您可以将服务注入父组件,然后通过子组件上的 @Input 将其传递给子组件。
【解决方案2】:

InjectionToken 可以替代已弃用的OpaqueToken

export const AuthenticationProvider = new InjectionToken(
  "AuthenticationProvider",
  { providedIn: "root", factory: () => new CognitoAuthenticationProvider() }
);

...

@Injectable()
export class CognitoAuthenticationProvider implements IAuthenticationProvider {

...

@Injectable({
  providedIn: "root"
})
export class AuthenticationService {
  constructor(
    @Inject(AuthenticationProvider)
    private authenticationProvider: IAuthenticationProvider,
    private http: HttpClient
  ) {}

【讨论】:

  • 如果CognitoAuthenticationProvider 有其他依赖项由 DI 解决怎么办?你如何在 InjectionToken 中解决这个问题?
猜你喜欢
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-25
  • 2018-08-30
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多