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

时间:2016-12-28 16:33:00

标签: angular typescript dependency-injection

假设我有一个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();
    }
}

我认为这在技术上是可行的,但是必须有更好的方法来进行动态依赖注入。

5 个答案:

答案 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回答,并创建一个导入服务的逻辑,而不必将名称声明为数组对象。

基本上,我们只需要传递服务的pathname,其余的几乎相同。

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