2016-05-03 28 views
20

Zastanawiam się, czy istnieje odpowiedni sposób na wstrzykiwanie interfejsów w Angular2? (poniżej)Czy można wtrysnąć interfejs za pomocą kątowego 2?

Myślę, że jest to związane z brakującym dekoratorem @Injectable() w interfejsie, ale wygląda na to, że jest to niedozwolone.

Pozdrawiam.

Kiedy CoursesServiceInterface jest zaimplementowana jako interfejs, kompilator maszynopis narzeka "CoursesServiceInterface nie można znaleźć nazwy":

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

ale z CoursesServiceInterface jako interfejs:

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

Gdy usługa jest klasą, kompilator TypeScript nie narzeka już:

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

Odpowiedz

39

Nie, interfejsy nie są obsługiwane dla DI. Z interfejsami TypeScript nie są już dostępne w środowisku wykonawczym, tylko statycznie i dlatego nie można ich używać jako tokenów DI.

Alternatywnie można użyć ciągi jako klucze lub InjectionToken

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

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

i wstrzyknąć go jak

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

Zobacz także https://angular.io/api/core/InjectionToken

+0

useClass: było to, czego brakuje ... szkoda, że ​​nie jest w oficjalnym poradniku – Aligned

+1

https://angular.io/docs/ts/ latest/cookbook/dependency-injection.html #! # usevalue –

2

Zastosowanie OpaqueToken interfejsy nie są obsługiwane przez DI, beacause sama Javascript nie interfejsów. Jednym ze sposobów na zrobienie tego w Angular 2 jest użycie 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; 
} 

Mam nadzieję, że to może pomóc.

+0

OpaqueToken jest przestarzały w Angular v5, z '' 'InjectionToken''' jako zamiennikiem – Ryan

-2

Moje doświadczenie (pochodzące z rozwoju backendu Java) do dev przodu to:

Jeśli mówimy o "interfejsie", mam duże oczekiwania, że ​​główna zasada używania interfejsu jest zapewniona przez języki, które oferują interfejs. Który jest: "kod przeciwko interfejsowi nie przeciwko implementacji".

Wydaje się to nie być zapewnione przez maszynopis/kątowy2. (chyba, że ​​nie powinni jeszcze używać interfejsu słownego).

Jaka była moja sprawa (ostrzeżenie: uczę angular2 więc moje obejście może wydawać się brzydki do zaawansowanych użytkowników):
Składnik A1 ma składnik dziecko B.
składnik b powinien znać rodziców i wywołać metodę na rodzic.
Tak komponent B otrzymuje obiekt nadrzędny za pośrednictwem DependencyInjection w jego konstruktorze.

constructor(private a: A1Component) {} 

Wszystko w porządku.
Niż rzeczy się komplikują.
Kolejnym składnikiem A2 może być element nadrzędny komp. B.
Idealnie powinienem wstrzyknąć w B interfejs (nie implementację), który jest realizowany zarówno przez A1 jak i A2 (byłby to oczywiście w świecie java).
Niż B będzie działać z tym interfejsem. W razie potrzeby Typekast na przykład do A2 spowodowałby, że B byłby świadomy, gdyby jego instancja rzeczywiście była A2, czy nie.

Mówię o prostych komponentach/klasach, nie usługach (widzę, że większość rozwiązań odnosi się do usług).
Próbowałem użyć @Host(), @Injectable(), OpaqueToken, Providers, ale zawsze był błąd. Kiedy w końcu wydawało się, że działa: w rzeczywistości obiekt wstrzyknięty do Komponentu B był pustym obiektem, a nie rodzicem - może niesłusznie został użyty do dostawców i utworzono nowy pusty obiekt zamiast wstrzykiwania obiektu macierzystego.

Co zrobiłem pod koniec: Nie korzystałem z interfejsu.
Utworzono zwykłą klasę podstawową dla A1 i A2 - nazwijmy ją ABase.
Komponent B zachowa odwołanie do tej klasy bazowej. Odniesienia byłyby ustawione w konstruktorze jak to:

//BComponent: 
parent: ABase;  

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

Tak, to dziwne, obejście, nie miły (pochodzących z JAVA myślenia, zgadzam się) - ale po prostu zabrakło czasu i był rozczarowany „interface "Rzecz.

22

Powodem, dla którego nie można korzystać z interfejsów, jest fakt, że interfejs jest artefaktem czasu projektowania w języku TypeScript. JavaScript nie ma interfejsów. Interfejs TypeScript znika z wygenerowanego kodu JavaScript. Nie ma informacji o typie interfejsu, które można znaleźć w Angular podczas runtime.


Rozwiązanie 1:

Najprostszym rozwiązaniem jest po prostu zdefiniować abstrakcyjną klasę, która implementuje interfejs. Często i tak potrzebujesz klas abstrakcyjnych.

Interfejs:

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

export interface ProcessEngine { 

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

    getRoles(): Role[]; 
} 

Streszczenie Klasa:

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

export abstract class ProcessEngineService implements ProcessEngine { 

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

    abstract getRoles(): Role[]; 

} 

Beton Klasa:

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

} 

Teraz można określić z dostawcą jak zwykle:

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

Rozwiązanie 2:

Oficjalna dokumentacja kątowej sugerują użycie InjectionToken, podobny do OpaqueToken.Oto przykład:

interfejs i klasa:

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

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

Zdefiniuj Token:

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

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

zarejestrować dostawcę zależnościach użyciu obiektu InjectionToken, na przykład w app.module.ts:

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

Następnie można wprowadzić obiekt konfiguracyjny do dowolnego konstruktora, który potrzebuje to, z pomocą w @Inject dekoratora:

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

Dla tych, którzy atakują Angular 4, którzy znaleźli tę odpowiedź, Rozwiązanie 2 jest zdecydowanie drogą iść. Dzięki tej konfiguracji wszelkiego rodzaju fałszywe klasy mogą być wstrzykiwane dla testów jednostkowych i podobnych, po prostu zmieniając 'providerów' na coś w stylu' providerów: [{provide: APP_CONFIG, useClass: AppConfigMockClass}] ' – Weikardzaena

+1

To jest niesamowite. Szybko zauważ, że jeśli zdefiniujesz token w tym samym pliku, co interfejs, możesz otrzymać niepotrzebne ostrzeżenia: https://github.com/angular/angular-cli/issues/2034 – EvanM

+0

@Weikardzaena W przypadku testowania zmieniasz dostawców w rzeczywistym AppModule, czy też można je ustawić w samych testach (lub używając środowiska np. test, dev, prod)? – Ryan