我想知道是否有一种在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; };
}
答案 0 :(得分:69)
不,DI不支持接口。由于TypeScript接口在运行时不再可用,因此只能静态使用,因此不能用作DI令牌。
或者,您可以将字符串用作键或InjectionToken
<击> 撞击>
<击>provide('CoursesServiceInterface', {useClass: CoursesServiceMock}) // old
击> <击> 撞击>
providers: [{provide: 'CoursesServiceInterface', useClass: CoursesServiceMock}]
并像
一样注入它constructor(@Inject('CoursesServiceInterface') private coursesService:CoursesServiceInterface) {}
答案 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) }],