基于@Input()的Angular 2动态依赖项注入
假设我有一个Angular 2 component指令,其中我希望组件使用的注入依赖项由@Input()确定 我想写一些类似于基于@Input()的Angular 2动态依赖项注入,angular,typescript,dependency-injection,Angular,Typescript,Dependency Injection,假设我有一个Angular 2 component指令,其中我希望组件使用的注入依赖项由@Input()确定 我想写一些类似于的东西,让TrendyDirective的实例使用serviceA,或者让它使用serviceB(如果我指定的话)。(这是我实际尝试的一个过于简化的版本) (如果你一开始认为这是一个糟糕的想法,我愿意接受这种反馈,但请解释原因。) 这里有一个例子,说明如何实现我的想法。在本例中,假设ServiceA和ServiceB都是可注入的,它们都通过具有“supercolfunct
的东西,让TrendyDirective的实例使用serviceA,或者让它使用serviceB(如果我指定的话)。(这是我实际尝试的一个过于简化的版本)
(如果你一开始认为这是一个糟糕的想法,我愿意接受这种反馈,但请解释原因。)
这里有一个例子,说明如何实现我的想法。在本例中,假设ServiceA和ServiceB都是可注入的,它们都通过具有“supercolfunction”来实现iSeries功能
@Component({
selector: 'trendy-directive',
...
})
export class TrendyDirective implements OnInit {
constructor(
private serviceA: ServiceA,
private serviceB: ServiceB){}
private service: iService;
@Input() use: string;
ngOnInit() {
switch (this.use){
case: 'serviceA': this.service = this.serviceA; break;
case: 'serviceB': this.service = this.serviceB; break;
default: throw "There's no such thing as a " + this.use + '!';
}
this.service.superCoolFunction();
}
}
我认为这在技术上是可行的,但必须有更好的方法来进行动态依赖注入。的确如此
// can be a service also for overriding and testing
export const trendyServiceMap = {
serviceA: ServiceA,
serviceB: ServiceB
}
constructor(private injector: Injector) {}
...
ngOnInit() {
if (trendyServiceMap.hasOwnProperty(this.use)) {
this.service = this.injector.get<any>(trendyServiceMap[this.use]);
} else {
throw new Error(`There's no such thing as '${this.use}'`);
}
}
//也可以是覆盖和测试的服务
导出常量trendyServiceMap={
服务A:服务A,
服务B:服务B
}
构造函数(专用注入器:注入器){}
...
恩戈尼尼特(){
if(trendyServiceMap.hasOwnProperty(this.use)){
this.service=this.injector.get(trendyServiceMap[this.use]);
}否则{
抛出新错误(`this.use}`之类的东西不存在,${this.use}`);
}
}
在@angular/core模块中有一个名为Inject的服务。通过@Inject,您可以实现其他注入方式。但这只能在构造函数中完成
因此,您需要将组件的输入放入@component decorator的输入数组中(不要在类中使用@Input decorator),然后将该输入变量注入构造函数。通常,Angular2文档中描述了相同的方法: 您必须在构造函数中注入
Injector
,并在@Component
注释的提供者
属性中列出所有服务。然后您可以injector.get(type)
,其中type
将从您的@输入中解析。根据文档,服务
在您请求之前不会被实际注入(.get()
)。我想进一步回答,创建一个导入服务的逻辑,而不必将名称声明为数组对象
基本上,我们只需传入服务的路径
和名称
,其余几乎相同
public _dynamicService: any;
dynamicDI(service_path, service_name){
import(service_path).then(s => {
this._dynamicService = this.injector.get<any>(s['service_name']);
})
}
在这里,你有一个方法,有点复杂,但工作像一个魅力
在共享模块和多个自定义实现中设置默认搜索服务。
而不必明确引用所有可能的实现
接口和默认实现
使用InjectionToken创建服务实现列表
自定义实现(可能在另一个模块上)
为自定义实现添加令牌
最后,在您的组件中
我也会投票支持注入:您是否对其父组件中的不同服务多次使用trendy指令,还是动态切换它?如果没有,父组件可以简单地提供正确的服务,而不需要实现复杂的逻辑@Component({selector:'i-use-trendy-directive',providers:[{provide:MyServiceInterfase,useClass:myserviceinpl}]}
thenconstructor(私有myService:MyServiceInterface){}
在我的情况下,我会在多个不同服务的地方使用该指令。谢谢。@Vinet'DEVIN'Dev这很有趣。关于decorator中输入数组的文档似乎很少。听起来@Component
decorator中的输入在组件生命周期中比那些指定的更早得到处理用@Input
装饰器填充。你知道什么时候吗?我刚刚测试了@Component
装饰器的输入,不幸的是,它们还没有在构造函数中填充(但它们是在我们到达ngOnInit时填充的,就像使用@Input
声明的一样)。您是否尝试过直接使用服务,而不是先尝试注入服务?例如,只使用@input从父组件获取服务变量的引用,然后不注入就使用它?我也尝试了我回答的方法,但似乎父组件在子组件之后初始化。因为4.0get
是dep重述。我指的是签名。请看,使用类型调用泛型方法总是更好的。我不确定是否会删除此签名。它是为了让用户知道使用泛型更好。无论如何,我更新了答案,感谢关注。通常我不必在解决方案中彻底键入,而是专注于提供g太好了。谢谢你,你的回答帮了我很大的忙!我试过了,但是“没有找到ComponentA的提供程序”,而我在提供程序列表中提到了ComponentA。有什么想法吗?
public _dynamicService: any;
dynamicDI(service_path, service_name){
import(service_path).then(s => {
this._dynamicService = this.injector.get<any>(s['service_name']);
})
}
this._dynamicService['your_function_name']().subscribe(res=> { console.log(res) } );
export interface ISearch {
searchByTerm(textInput: string);
}
export class DefaultSearch implements ISearch {
searchByTerm(textInput: string) { console.log("default search by term"); }
}
// Keep list of token, provider will give a implementation for each of them
export const SearchServiceTokens: Map<string, InjectionToken<ISearch>> = new Map();
// Add File service implementation token
SearchServiceTokens.set('default', new InjectionToken<ISearch>('default'));
providers: [
...
// Default implementation service
{
provide: SearchServiceTokens.get('default'),
useClass: DefaultSearch
}
]
export class Component1Search implements ISearch {
searchByTerm(textInput: string) { console.log("component1 search by term"); }
}
SearchServiceTokens.set('component1', new InjectionToken<ISearch>('component1'));
providers: [
...
// Other implementation service
{
provide: SearchServiceTokens.get('component1'),
useClass: Component1Search
}
]
@Input useService;
searchService: ISearch;
constructor(private injector: Injector) {
// Use default if none provided
let serviceToUse = 'default';
if (null !== this.useService) { serviceToUse = this.useService; }
this.searchService = this.injector.get(SearchServiceTokens.get(serviceToUse));
}