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

时间:2016-05-03 11:11:12

标签: interface angular inject

我想知道是否有一种在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 })
  ]);

但是使用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; };
}

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

5 个答案:

答案 0 :(得分:69)

不,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

答案 1 :(得分:42)

您不能使用接口的原因是因为接口是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:

Angular的官方文档建议使用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;
}

答案 2 :(得分:3)

角度9的替代解决方案

jP <- j %>%
  summarise(median = median(con), mean = mean(con), SD = sd(con))

创建一个抽象类

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

    return todos;
  }
}

在组件中使用

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

@Injectable()
export abstract class TodoListService {
  abstract getTodos(): Todo[];
}

答案 3 :(得分:2)

使用OpaqueToken,DI不支持接口,因为Javascript本身没有接口。在Angular 2中执行此操作的一种方法是使用OpaqueToken。 https://angular.io/docs/ts/latest/guide/dependency-injection.html

var src = $(".a img").attr('src');

if($(".thumb img").attr('src') == src ) {
$(".thumb").hide();
}

我希望这可以提供帮助。

答案 4 :(得分:-2)

我的经验(来自Java后端开发)到frontend dev中如下。

如果我们谈论'界面',我强烈期望通过“提供”界面的语言确保使用界面的主要原则。 这是:'代码反对接口不反对实现'。

这似乎无法通过typescript / angular2来保证。 (也许他们不应该使用word界面。)

我的情况是什么(警告:我正在学习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世界的想法,我同意) - 但我只是没时间而且对'界面'事情感到失望。

更新

我重新考虑之前的答案(这是糟糕的设计,丑陋......是我刚开始的角度)

现在,Angular的文档清楚地描述了这个问题:找到组件的父级。

无法使用界面 - 因为无法注入界面。

“寻找实现接口的组件会更好。这是不可能的,因为TypeScript接口从已转换的JavaScript中消失,它不支持接口。没有工件可供查找。”

不能使用base class of possible parents ......(这是我以前绝望,不好回答的根本原因)

什么有效?技术:find a parent by its class-interface

主要是:

Child B查看普通父Parent(可以是A1Component或A2Component)

export class BComponent {
  name = 'B';
  constructor( @Optional() public parent: Parent ) { }
}

每个可能的父组件使用class-interface提供Parent(在组件级别!!!):

providers: [{ provide: Parent, useExisting: forwardRef(() => A1Component) }],