2016-05-03 13 views
20

我想知道在Angular2中是否有正确的方法来注入接口? (参见下面)是否可以使用angular2注入接口?

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

问候。

当CoursesServiceInterface被作为接口来实现,打字稿编译器抱怨 “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 }) 
    ]); 

但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; }; 
} 

当服务是一流的, 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; }; 
} 

回答

39

不,DI不支持接口。随着TypeScript接口在运行时不再可用,只能静态地使用,因此不能用作DI令牌。

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

provide('CoursesServiceInterface', {useClass: CoursesServiceMock}) // old 

providers: [{provide: 'CoursesServiceInterface', useClass: CoursesServiceMock}] 

,并注入它像

constructor(@Inject('CoursesServiceInterface') private coursesService:CoursesServiceInterface) {} 

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

+0

useClass:是我失踪了......太糟糕了,是不是在官方教程 – Aligned

+1

https://angular.io/docs/ts/最新版本/ cookbook/dependency-injection.html#!#usevalue –

2

使用OpaqueToken,接口不被支持DI,东阳的Javascript本身没有接口。在Angular 2中做到这一点的一种方法是使用OpaqueToken。 https://angular.io/docs/ts/latest/guide/dependency-injection.html

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

export let APP_CONFIG = new OpaqueToken('app.config'); 

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }] 

constructor(@Inject(APP_CONFIG) config: AppConfig) { 
    this.title = config.title; 
} 

我希望这可以帮助。

+0

OpaqueToken在Angular v5中被弃用,使用''''InjectionToken'''作为替换者 – Ryan

-2

我的经验(来自java后端开发)到前端开发如下。

如果我们谈论'接口',我强烈期望使用接口的主要原则由'提供'接口的语言保证。 这是:'代码反对接口不违反实施'。

这似乎不能通过typescript/angular2来保证。 (比他们不应该使用文字界面,也许)。

什么是我的情况下(警告:我在学习angular2所以我的解决办法可能似乎丑到高级用户):
组件A1有一个子组件B.
B组分应该知道父母和调用方法家长。
所以组件B通过它的构造函数中的DependencyInjection接收父代。

constructor(private a: A1Component) {} 

一切都很好。
比事情变得复杂。
另一个组件A2可以是comp的父项。 B.
理想情况下,我应该在B中注入一个由A1和A2实现的接口(不是实现)(这在Java世界中当然是这样的)。
比B可以使用这个接口。如果需要的话,以A2为例的类型转换会让B知道他是否真的是A2。

我讲的是简单的组件/类,而不是服务(我发现大多数解决方案都是指服务)。
我试图使用@Host(),@Injectable(),OpaqueToken,Providers,但总是出现错误。当最后它似乎工作:实际上在组件B中注入的对象是一个空对象,而不是父对象 - 可能我错误地用于提供程序,并创建一个新的空对象而不是注入父对象。

我到底做了什么:我没有使用接口。
我为A1和A2创建了一个普通的基类 - 让我们称之为ABase。
组件B将保留对此基类的引用。参考将在构造函数中设置为这样:

//BComponent: 
parent: ABase;  

constructor(@Optional parentA1: A1Component, @Optional parentA2: A2Component) { 
    if(parentA1) 
     this.parent = parentA1; 
    else 
     this.parent = parentA2 
} 

是的,这是一个奇怪的解决办法,不是很好(从Java世界想来,我同意) - 但我只是跑出来的时候,是失望的“接口'的东西。

22

您无法使用接口的原因是因为接口是TypeScript设计时工件。 JavaScript没有接口。 TypeScript界面​​从生成的JavaScript中消失。没有任何界面类型信息可供Angular在运行时查找。


解决方案1:

最简单的解决方案只是以限定它实现接口的抽象类。通常,无论如何你都需要一个抽象类。

接口:

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

export interface ProcessEngine { 

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

    getRoles(): Role[]; 
} 

抽象类:

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

export abstract class ProcessEngineService implements ProcessEngine { 

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

    abstract getRoles(): Role[]; 

} 

具体类:

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[] {...} 

} 

现在你可以定义你的供应商通常一样:

@NgModule({ 
     ... 
     providers: [ 
     ..., 
     {provide: ProcessEngineService, useClass: WebRatioEngineService} 
     ] 
}) 

解决方案2:

角的官方文档建议使用InjectionToken,类似OpaqueToken。这里是例子:

你的接口和类:

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

export const HERO_DI_CONFIG: AppConfig = { 
    apiEndpoint: 'api.heroes.com', 
    title: 'Dependency Injection' 
}; 

定义您的令牌:

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

export let APP_CONFIG = new InjectionToken<AppConfig>('app.config'); 

使用InjectionToken对象,例如,在你的app.module.ts注册的依赖供应商:

providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }] 

比你可以将配置对象注入任何需要的构造函数它与@Inject装饰的帮助:

constructor(@Inject(APP_CONFIG) config: AppConfig) { 
    this.title = config.title; 
} 
+1

对于那些找到这个答案的Angular 4来说,解决方案2绝对是方式去。通过这种设置,只需将'providers'改为'providers'即可为任何类型的模拟类注入单元测试等。[{provide:APP_CONFIG,useClass:AppConfigMockClass}] – Weikardzaena

+1

这太棒了。请注意,如果您在与界面相同的文件中定义一个标记,则可能会收到不必要的警告:https://github.com/angular/angular-cli/issues/2034 – EvanM

+0

@Weikardzaena对于测试,您是否更改提供程序在实际的AppModule中,还是可以在测试本身中设置它们(或者使用Environment,例如test,dev,prod)? – Ryan

相关问题