2016-05-03 39 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

相關問題