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

bsxbgnwa  于 2023-03-24  发布在  TypeScript
关注(0)|答案(6)|浏览(152)

假设我有一个Angular 2 component-directive,我希望组件使用的注入依赖项由@Input()确定。
我想写一个类似<trendy-directive use="'serviceA'">的东西,让TrendyDirective的示例使用serviceA,或者让它使用serviceB(如果我指定的话)。
(If你认为这是一个可怕的想法开始,我开放的反馈,但请解释为什么。
这里有一个例子来说明如何实现我所想的。在这个例子中,假设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();
    }
}

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

kx7yvsdv

kx7yvsdv1#

是的

// 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}'`);
    }
}
nuypyhwy

nuypyhwy2#

一般来说,Angular2文档中描述了相同的方法:注射器组件

@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,并在@Component annotation的providers属性中列出所有服务。然后您可以injector.get(type),其中type将从@Input解析。根据文档,Service实际上不会注入,直到您要求它(.get())。

njthzxwz

njthzxwz3#

我想进一步介绍Estus Flask answer,创建一个导入服务的逻辑,而不必将名称声明为数组对象。
基本上,我们只需要传入服务的pathname,其余的几乎相同。

public _dynamicService: any;

dynamicDI(service_path, service_name){
    import(service_path).then(s => {

      this._dynamicService = this.injector.get<any>(s['service_name']);

    })
}

现在,您可以访问dynamicService中的函数,如下所示:
(Let假设我们需要的服务中有一个http可观察的fn)

this._dynamicService['your_function_name']().subscribe(res=> { console.log(res) } );
9cbw7uwe

9cbw7uwe4#

在这里你有一个方法,有点复杂,但工作像一个魅力!
在一个共享模块和多个自定义实现中设置默认搜索服务。并且不必显式引用所有可能的实现。
接口和默认实现

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

iq0todco5#

在@angular/core模块中有一个名为Inject的服务。使用@Inject可以实现替代的注入方式。但这只能在构造函数中完成。
所以你需要将组件的输入放入@component装饰器的inputs数组中(不要在类中使用@Input装饰器),然后将该输入变量注入构造函数中。

v440hwme

v440hwme6#

这可能为时已晚,但可能有一些有趣和整洁的解决方案动态注入的一些条件。主要思想是使用服务 Package 器和添加方法或getter来获取必要的服务的适当示例:

@Injectable({
    providedIn: 'root',
})
export class CredentialsServiceImp {      
    constructor(           
        private visitorService: VisitorCredentialsServiceImp,
        private employeeService: EmployeeCredentialsServiceImp
    ) {

    }
    instance(userType:UserType): UserCredentialsService {
        return userType == UserType.employee ? this.employeeService : this.visitorService;
    }

}

这两个服务都实现了抽象类UserCredentialsService,instance getter通过条件适当的示例获得。

export class Component implements OnInit {
      @Input() userType : UserType;    
    constructor(           
        private credentials: CredentialsServiceImp  
    ) {
        
    }

     ngOnInit():  {
      const service = this.credentials.instance(this.userType);
    }  
   
   
}

相关问题