Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
基于@Input()的Angular 2动态依赖项注入_Angular_Typescript_Dependency Injection - Fatal编程技术网

基于@Input()的Angular 2动态依赖项注入

基于@Input()的Angular 2动态依赖项注入,angular,typescript,dependency-injection,Angular,Typescript,Dependency Injection,假设我有一个Angular 2 component指令,其中我希望组件使用的注入依赖项由@Input()确定 我想写一些类似于的东西,让TrendyDirective的实例使用serviceA,或者让它使用serviceB(如果我指定的话)。(这是我实际尝试的一个过于简化的版本) (如果你一开始认为这是一个糟糕的想法,我愿意接受这种反馈,但请解释原因。) 这里有一个例子,说明如何实现我的想法。在本例中,假设ServiceA和ServiceB都是可注入的,它们都通过具有“supercolfunct

假设我有一个Angular 2 component指令,其中我希望组件使用的注入依赖项由@Input()确定

我想写一些类似于
的东西,让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}]}
then
constructor(私有myService:MyServiceInterface){}
在我的情况下,我会在多个不同服务的地方使用该指令。谢谢。@Vinet'DEVIN'Dev这很有趣。关于decorator中输入数组的文档似乎很少。听起来
@Component
decorator中的输入在组件生命周期中比那些指定的更早得到处理用
@Input
装饰器填充。你知道什么时候吗?我刚刚测试了
@Component
装饰器的输入,不幸的是,它们还没有在构造函数中填充(但它们是在我们到达ngOnInit时填充的,就像使用
@Input
声明的一样)。您是否尝试过直接使用服务,而不是先尝试注入服务?例如,只使用@input从父组件获取服务变量的引用,然后不注入就使用它?我也尝试了我回答的方法,但似乎父组件在子组件之后初始化。因为4.0
get
是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));
    }