2016-05-03 22 views
20

tôi tự hỏi nếu có một cách thích hợp để tiêm giao diện trong Angular2? (xem bên dưới)Có thể tiêm giao diện với angular2 không?

Tôi nghĩ rằng điều này có liên quan với trang trí bị thiếu @Injectable() trên giao diện, nhưng có vẻ như điều này không được phép.

Trân trọng.

Khi CoursesServiceInterface được thực hiện như một giao diện, trình biên dịch nguyên cảo phàn nàn "CoursesServiceInterface không thể tìm thấy cái tên":

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

nhưng với CoursesServiceInterface như một giao diện:

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

Khi dịch vụ là một lớp học, trình biên dịch TypeScript không phàn nàn nữa:

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

Trả lời

39

Không, giao diện không được hỗ trợ cho DI. Với các giao diện TypeScript không có sẵn trong thời gian chạy nữa, chỉ tĩnh và do đó không thể được sử dụng như các thẻ DI.

Hoặc bạn có thể sử dụng chuỗi như là chìa khóa hoặc InjectionToken

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

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

và tiêm nó như

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

Xem thêm https://angular.io/api/core/InjectionToken

+0

useClass: là những gì tôi đã mất tích ... quá xấu mà không có trong hướng dẫn chính thức – Aligned

+1

https://angular.io/docs/ts/ mới nhất/cookbook/dependency-injection.html #! # usevalue –

2

Sử dụng OpaqueToken, giao diện không được hỗ trợ bởi DI, vì chính Javascript không có giao diện. Một cách để làm điều này trong Angular 2 là sử dụng 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; 
} 

Tôi hy vọng điều này có thể hữu ích.

+0

OpaqueToken không được dùng nữa trong Angular v5, với '' 'InjectionToken''' là thay thế – Ryan

-2

Trải nghiệm của tôi (đến từ phát triển phụ trợ java) vào bản phát triển giao diện người dùng là như sau.

Nếu chúng ta nói về 'giao diện', tôi có kỳ vọng mạnh mẽ rằng nguyên tắc chính của việc sử dụng giao diện được đảm bảo bởi giao diện ngôn ngữ 'cung cấp'. Đó là: 'mã chống lại giao diện không chống lại việc triển khai'.

Điều này dường như không được đảm bảo bởi loại/angular2. (hơn là họ không nên sử dụng giao diện từ, có thể).

trường hợp của tôi là gì (cảnh báo: Tôi đang học angular2 nên workaround của tôi có thể có vẻ xấu xí cho người dùng nâng cao):
Component A1 có một thành phần con B.
Hợp phần B nên biết phụ huynh và gọi một phương thức trên cha mẹ.
Vì vậy, thành phần B nhận phụ huynh qua DependencyInjection trong hàm tạo của nó.

constructor(private a: A1Component) {} 

Mọi thứ đều ổn.
Mọi thứ trở nên phức tạp.
Một thành phần A2 khác có thể là phụ huynh của comp. B.
Lý tưởng nhất tôi nên tiêm vào B một giao diện (không thực hiện) được thực hiện bởi cả A1 và A2 (điều này sẽ tự nhiên trong thế giới java).
Than B sẽ hoạt động với giao diện này. Nếu cần thiết, một typecast để A2 ví dụ sẽ làm cho B nhận thức nếu dụ ông có thực sự là A2 hay không.

Tôi nói về các thành phần/lớp học đơn giản, không phải dịch vụ (tôi thấy rằng hầu hết các giải pháp đều đề cập đến các dịch vụ).
Tôi đã cố gắng sử dụng @Host(), @Injectable(), OpaqueToken, Nhà cung cấp nhưng luôn có lỗi. Khi cuối cùng nó dường như hoạt động: trong thực tế đối tượng được tiêm trong Component B là một đối tượng rỗng, không phải là cha mẹ - có thể tôi đã sử dụng sai cho các nhà cung cấp và một đối tượng trống mới được tạo ra thay vì tiêm đối tượng cha.

Điều tôi đã làm cuối cùng: Tôi không sử dụng giao diện.
Tôi đã tạo một lớp cơ bản đơn giản cho A1 và A2 - hãy gọi nó là ABase.
Thành phần B sẽ giữ tham chiếu đến lớp cơ sở này. Tài liệu tham khảo sẽ được thiết lập trong constructor như thế này:

//BComponent: 
parent: ABase;  

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

Vâng, đó là một cách giải quyết khác lạ, không thoải mái (đến từ suy nghĩ thế giới java, tôi đồng ý) - nhưng tôi chỉ cần chạy ra khỏi thời gian và đã thất vọng về 'giao diện ' Điều.

22

Lý do bạn không thể sử dụng giao diện là vì giao diện là một tạo phẩm thời gian thiết kế TypeScript. JavaScript không có giao diện. Giao diện TypeScript biến mất khỏi JavaScript được tạo ra. Không có thông tin kiểu giao diện nào được để lại cho Angular để tìm trong thời gian chạy.


Giải pháp 1:

Giải pháp đơn giản nhất là chỉ để xác định một lớp trừu tượng mà thực hiện giao diện. Thông thường, bạn cần một lớp trừu tượng.

Interface:

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

export interface ProcessEngine { 

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

    getRoles(): Role[]; 
} 

Abstract Class:

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

export abstract class ProcessEngineService implements ProcessEngine { 

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

    abstract getRoles(): Role[]; 

} 

bê tông Class:

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

} 

Bây giờ bạn có thể xác định nhà cung cấp của bạn như bình thường:

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

Giải pháp 2:

Các tài liệu chính thức của Angular đề nghị sử dụng InjectionToken, tương tự như OpaqueToken.Dưới đây là ví dụ:

giao diện của bạn và lớp:

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

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

Xác định mã số của bạn:

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

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

Đăng ký nhà cung cấp phụ thuộc bằng cách sử dụng đối tượng InjectionToken, ví dụ như trong app.module.ts của bạn:

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

Hơn bạn có thể đưa đối tượng cấu hình vào bất kỳ hàm tạo nào cần nó, với sự giúp đỡ của một nhà thiết @Inject:

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

Đối với những người nhắm mục tiêu Angular 4, người đã tìm thấy câu trả lời này, Solution 2 chắc chắn là cách đi. Với thiết lập này, bất kỳ loại lớp mô phỏng nào cũng có thể được tiêm để kiểm thử đơn vị và tương tự bằng cách thay đổi 'nhà cung cấp' thành một cái gì đó như' nhà cung cấp: [{cung cấp: APP_CONFIG, useClass: AppConfigMockClass}] ' – Weikardzaena

+1

Điều này thật tuyệt vời. Lưu ý nhanh rằng nếu bạn xác định mã thông báo trong cùng một tệp với giao diện, bạn có thể nhận được cảnh báo không cần thiết: https://github.com/angular/angular-cli/issues/2034 – EvanM

+0

@Weikardzaena Để kiểm tra, bạn có thay đổi nhà cung cấp không trong AppModule thực tế, hoặc có thể được đặt trong các bài kiểm tra tự (hoặc sử dụng môi trường ví dụ như thử nghiệm, dev, prod)? – Ryan

Các vấn đề liên quan