是否可以使用angular2注入接口?

use*_*220 52 interface inject angular

我想知道是否有一种在Angular2中注入接口的正确方法?(参见下文)

我认为这与接口上缺少的@Injectable()装饰器有关,但似乎不允许这样做.

问候.

当CoursesServiceInterface作为接口实现时,TypeScript编译器会抱怨"CoursesServiceInterface找不到名称":

import {CoursesServiceInterface} from './CoursesService.interface';
import {CoursesService} from './CoursesService.service';
import {CoursesServiceMock} from './CoursesServiceMock.service';
bootstrap(AppComponent, [
  ROUTER_PROVIDERS, 
  GlobalService,
  provide(CoursesServiceInterface, { useClass: CoursesServiceMock })
  ]);
Run Code Online (Sandbox Code Playgroud)

但是使用CoursesServiceInterface作为接口:

import {Injectable} from 'angular2/core';
import {Course} from './Course.class';
//@Injectable()
export interface CoursesServiceInterface {
    getAllCourses(): Promise<Course[]>;//{ return null; };
    getCourse(id: number): Promise<Course>;// { return null; };
    remove(id: number): Promise<{}>;// { return null; };
}
Run Code Online (Sandbox Code Playgroud)

当service是一个类时,TypeScript编译器不再抱怨:

import {Injectable} from 'angular2/core';
import {Course} from './Course.class';
@Injectable()
export class CoursesServiceInterface {  
    getAllCourses() : Promise<Course[]> { return null; };
    getCourse(id: number) :Promise<Course> { return null; };
    remove (id: number) : Promise<{}> { return null; };
}
Run Code Online (Sandbox Code Playgroud)

Gün*_*uer 81

不,DI不支持接口.由于TypeScript接口在运行时不再可用,因此只能静态使用,因此不能用作DI令牌.

或者,您可以使用字符串作为键或 InjectionToken

provide('CoursesServiceInterface', {useClass: CoursesServiceMock}) // old
Run Code Online (Sandbox Code Playgroud)

providers: [{provide: 'CoursesServiceInterface', useClass: CoursesServiceMock}]
Run Code Online (Sandbox Code Playgroud)

并注入它

constructor(@Inject('CoursesServiceInterface') private coursesService:CoursesServiceInterface) {}
Run Code Online (Sandbox Code Playgroud)

另请参见https://angular.io/api/core/InjectionToken


Tob*_*obi 52

您无法使用接口的原因是因为接口是TypeScript设计时工件.JavaScript没有接口.TypeScript接口从生成的JavaScript中消失.Angular没有留下接口类型信息以便在运行时查找.


解决方案1:

最简单的解决方案就是定义一个实现接口的抽象类.通常,无论如何你都需要一个抽象类.

接口:

import {Role} from "../../model/role";

export interface ProcessEngine {

     login(username: string, password: string):string;

     getRoles(): Role[];
}
Run Code Online (Sandbox Code Playgroud)

抽象类:

import {ProcessEngine} from "./process-engine.interface";

export abstract class ProcessEngineService implements ProcessEngine {

    abstract login(username: string, password: string): string;

    abstract getRoles(): Role[];

}
Run Code Online (Sandbox Code Playgroud)

具体类:

import { Injectable } from '@angular/core';
import {ProcessEngineService} from "./process-engine.service";

@Injectable()
export class WebRatioEngineService extends ProcessEngineService {

    login(username: string, password: string) : string {...}

    getRoles(): Role[] {...}

}
Run Code Online (Sandbox Code Playgroud)

现在您可以像往常一样定义您的提供商:

@NgModule({
      ...
      providers: [
        ...,
        {provide: ProcessEngineService, useClass: WebRatioEngineService}
      ]
})
Run Code Online (Sandbox Code Playgroud)

解决方案2:

Angular的官方文档建议使用InjectionToken,类似于OpaqueToken.这是一个例子:

你的界面和课程:

export interface AppConfig {
   apiEndpoint: string;
   title: string;
}

export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'
};
Run Code Online (Sandbox Code Playgroud)

定义您的令牌:

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

export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
Run Code Online (Sandbox Code Playgroud)

使用InjectionToken对象注册依赖项提供程序,例如在app.module.ts中:

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
Run Code Online (Sandbox Code Playgroud)

在@Inject装饰器的帮助下,您可以将配置对象注入任何需要它的构造函数中:

constructor(@Inject(APP_CONFIG) config: AppConfig) {
     this.title = config.title;
}
Run Code Online (Sandbox Code Playgroud)

  • 对于那些找到了这个答案的针对 Angular 4 的人来说,解决方案 2 绝对是要走的路。使用此设置,只需将 `providers` 更改为类似 `providers: [{ provide: APP_CONFIG, useClass: AppConfigMockClass }]` 的内容,即可注入任何类型的模拟类进行单元测试等 (4认同)
  • 解决方案 1 似乎比解决方案 2 更优雅。有人可以解释为什么解决方案 2 是首选吗? (2认同)
  • @kctang 因为解决方案 1 强制您创建一个不必要的抽象级别(抽象类毫无意义) (2认同)
  • (我之前评论的附录)使用抽象类作为基石而完全省略接口的一个缺点是,在极端情况下,服务需要实现两个“接口”,但它无法实现。所以从这个意义上说,界面确实仍然是必要的 (2认同)

San*_*isy 6

角度 9 的替代解决方案

@Injectable()
export class TodoListPublicService implements TodoListService {
  getTodos(): Todo[] {
    const todos: Todo[] = [
      {
        title: 'get groceries',
        description: 'eggs, milk, etc.',
        done: false
      }
    ];

    return todos;
  }
}
Run Code Online (Sandbox Code Playgroud)

创建一个抽象类

export interface Todo {
  title: string;
  description: string;
  done: boolean;
}

@Injectable()
export abstract class TodoListService {
  abstract getTodos(): Todo[];
}
Run Code Online (Sandbox Code Playgroud)

在组件中使用

providers: [
    { provide: TodoListService, useClass: TodoListPublicService }
  ]
export class TodoListComponent implements OnInit {
  todos: Todo[];

  constructor(private todoListService: TodoListService) { }

  ngOnInit() {
  }
Run Code Online (Sandbox Code Playgroud)