假设我有一个Angular 2组件指令,其中我希望组件使用的注入依赖项由@Input()确定。
我想写一些像<trendy-directive use="'serviceA'">
这样的东西,并让TrendyDirective的实例使用serviceA,或者让它使用serviceB,如果这是我指定的内容。 (这是我实际尝试做的过于简单的版本)
(如果您认为这是一个可怕的想法,我可以接受这些反馈,但请解释原因。)
以下是如何实现我所想到的一个例子。在这个例子中,假设ServiceA和ServiceB是注入式的,它们都通过一个&supercoolFunction来实现iService。
@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();
}
}
我认为这在技术上是可行的,但是必须有更好的方法来进行动态依赖注入。
答案 0 :(得分:10)
是
// 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}'`);
}
}
答案 1 :(得分:5)
通常,Angular2文档中描述了相同的方法:InjectorComponent
@Component({
providers: [Car, Engine, Tires, heroServiceProvider, Logger]
})
export class InjectorComponent {
car: Car = this.injector.get(Car);
heroService: HeroService = this.injector.get(HeroService);
hero: Hero = this.heroService.getHeroes()[0];
constructor(private injector: Injector) { }
}
您必须在构造函数中注入Injector
并列出providers
注释的@Component
属性中的所有服务。然后,您可injector.get(type)
,type
将从@Input
解析Service
。根据文档,.get()
在您提出要求之前并未实际注入(zip3
)。
答案 2 :(得分:2)
@ angular / core模块中有一个名为Inject的服务。使用@Inject,您可以实现另一种注射方式。但这只能在构造函数中完成。
因此,您需要将组件的输入放在@component装饰器的输入数组中(不要在类中使用@Input装饰器),然后在构造函数中注入该输入变量。
答案 3 :(得分:0)
我想进一步Estus Flask回答,并创建一个导入服务的逻辑,而不必将名称声明为数组对象。
基本上,我们只需要传递服务的path
和name
,其余的几乎相同。
public _dynamicService: any;
dynamicDI(service_path, service_name){
import(service_path).then(s => {
this._dynamicService = this.injector.get<any>(s['service_name']);
})
}
现在,您可以按以下方式访问dynamicService
内部的功能:
(假设我们在我们需要的服务中有一个http observable fn)
this._dynamicService['your_function_name']().subscribe(res=> { console.log(res) } );
答案 4 :(得分:0)
这里有一个方法,有点复杂,但效果很好!
在共享模块和多个自定义实现中设置默认搜索服务。 并且无需明确引用所有可能的实现。
<块引用>接口和默认实现
export interface ISearch {
searchByTerm(textInput: string);
}
export class DefaultSearch implements ISearch {
searchByTerm(textInput: string) { console.log("default search by term"); }
}
<块引用>
使用 InjectionToken 创建服务实现列表
// 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));
}