Angularjs 将特定服务实例注入到一个父组件中组件的多个实例中

Angularjs 将特定服务实例注入到一个父组件中组件的多个实例中,angularjs,angular,service,dependency-injection,components,Angularjs,Angular,Service,Dependency Injection,Components,我现在正在做一个非常大的项目,我们正在努力使我们的代码尽可能模块化。我们有多个Angular应用程序,我们创建了一个单独的通用UI组件库和一个API工具包,其中包含跨这些应用程序使用的通用服务 但是,在尝试构建需要与服务一起工作的公共组件时,我们遇到了问题。例如,我现在正在开发一个自动完成组件。该组件应该能够从远程源获取数据,并根据在组件的输入字段中键入的内容过滤结果 对于一个实例来说,这种实现非常简单。我将autocomplete服务注入到autocomplete组件的构造函数中,然后在父组件

我现在正在做一个非常大的项目,我们正在努力使我们的代码尽可能模块化。我们有多个Angular应用程序,我们创建了一个单独的通用UI组件库和一个API工具包,其中包含跨这些应用程序使用的通用服务

但是,在尝试构建需要与服务一起工作的公共组件时,我们遇到了问题。例如,我现在正在开发一个自动完成组件。该组件应该能够从远程源获取数据,并根据在组件的输入字段中键入的内容过滤结果

对于一个实例来说,这种实现非常简单。我将autocomplete服务注入到autocomplete组件的构造函数中,然后在父组件上提供它。这使我能够在使用服务时灵活地更改服务的实现细节,同时仍然能够创建一个可重用的组件,该组件可以与定义的接口一起工作

例如:

我们要在其中定义自动完成组件使用的接口的服务:

@Injectable()
export class AutocompleteService {
  public url: string = 'my-api-default';

  constructor(http: Http) {}

  fetch(): any {
    return this.http.get(this.url);
  }
}
自动完成组件实现:

@Component({
  selector: 'my-autocomplete',
  templateUrl: 'my-autocomplete.component.html'
})
export class MyAutocompleteComponent {
  constructor(private autocompleteService: AutocompleteService) {}

  getData() {
    return this.autocompleteService.fetch();
  }
  ...autocomplete logic...
}
现在我可以定义一个实现自动完成服务的bear服务。我可以将熊服务连接到我的自动完成组件,这样我就可以在我的表单中选择熊的种类

@Injectable()
export class BearService {
  public url: string = 'bear-url';

  constructor(http: Http){}

  fetch() {
    return this.http.get(this.url);
  }
}
接下来,我定义使用自动完成组件并提供熊服务以获取熊物种数据的父级

@Component({
  selector: 'my-form-component',
  template: `
    <form>
      <my-autocomplete [(ngModel)]="bear"></my-autocomplete>
      <button type="submit">Submit</button>
    </form>
  `,
  providers: [
    {provide: AutocompleteService, useClass: BearService}
  ]
})
export class MyFormComponent {
  ...component logic...
}
然后更新父模板和提供程序:

@Component({
  selector: 'my-form-component',
  template: `
    <form>
      <my-autocomplete [(ngModel)]="bear"></my-autocomplete>
      <my-autocomplete [(ngModel)]="beet"></my-autocomplete>
      <my-autocomplete [(ngModel)]="battleStarGallactica"></my-autocomplete>
      <button type="submit">Submit</button>
    </form>
  `,
  providers: [
    {provide: AutocompleteService, useClass: BearService},
    {provide: AutocompleteService, useClass: BeetService},
    {provide: AutocompleteService, useClass: BattlestarGalacticaService},
  ]
})
export class MyFormComponent {
  ...component logic...
}
@组件({
选择器:“我的表单组件”,
模板:`
提交
`,
供应商:[
{提供:自动完成服务,useClass:BearService},
{提供:自动完成服务,useClass:BeetService},
{提供:自动完成服务,useClass:BattleStarGalacticService},
]
})
导出类MyFormComponent{
…组件逻辑。。。
}
现在,我如何判断哪个自动完成组件要使用哪个服务

我知道我现在拥有的将始终使用最后一个为AutocompleteService提供的提供者,因为Angular DI框架就是这样工作的

我也知道我不能在这个问题上使用多个提供者,因为Angular只为
NG_校验器
NG_异步校验器
定义它们

那么,有人知道我如何解决我的问题吗?我不在乎问题本身是如何解决的,但我仍然需要能够:

  • 定义服务接口
  • 在可重用组件中创建服务接口的用户
  • 为我自己的需要创建一个实现原始接口的新服务实例
  • 能够在单个父组件中使用不同的服务实现来使用实现相同服务接口的多个组件

  • 我会将提供者移动到一个选择性应用的指令

    @Directive({
      selector: 'my-autocomplete[bear]',
      providers: [{ provide: AutoCompleteService, useClass: BearService}]
    })
    export class BearDirective {}
    
    @Directive({
      selector: 'my-autocomplete[battlestarGalactica]',
      providers: [{ provide: AutoCompleteService, useClass: BattlestarGalacticaService}]
    })
    export class BattelstarGalacticaDirective{}
    
    然后像这样使用它

    <form>
      <my-autocomplete bear [(ngModel)]="bear"></my-autocomplete>
      <my-autocomplete bear [(ngModel)]="beet"></my-autocomplete>
      <my-autocomplete battlestarGalactica [(ngModel)]="battleStarGallactica"></my-autocomplete>
      <button type="submit">Submit</button>
    </form>
    
    
    提交
    

    并从
    MyFormComponent

    中删除提供程序,谢谢您的回答!我要把它付诸实践,它不像我希望的那样优雅。现在,使用我们的库的人已经增加了创建新指令的步骤,并在创建新服务的同时将其添加到模块中。我很想看到一种直接在视图上注册提供者的方法,也许将来我们会找到一些很酷的方法来使用多个提供者并直接在视图上定义实例。我知道我们也有viewProviders,但这种情况有点不同。您需要在构造函数中注入子类(
    BattlestarGalacticaService
    ),否则DI现在可以知道注入什么了。您可以做的是
    {provide:'AutocompleteService',useValue:{bear:new BearService(),beet:new BeetService(),battlestarGalactica:new BattlestarGalacticaService()}
    然后在组件中添加
    构造函数(@Inject('AutocompleteService')private ac:any{}@Input()类型:string
    当您需要访问您使用的服务时,
    this.ac[键入]
    非常棒,非常完美。非常感谢Günter。如果
    AutoCompleteService
    (或其子类)需要注入依赖项,您也可以使用
    useFactory
    而不是
    useValue
    。我不知道如何在工厂中使用这种方法,而不需要更加复杂。在使用工厂时,如何配置组件以了解要使用的服务?函数providerFactor(http:http,someOpaqueToken){if(someOpaqueToken=='bear'){return new BearService();}否则if(someOpaqueToken=='beet'){return new BeetService(http);}如何从子级设置该令牌?似乎它必须在父级上设置,或者我必须创建一些应用程序范围的服务来从子级设置和更新它?
    <form>
      <my-autocomplete bear [(ngModel)]="bear"></my-autocomplete>
      <my-autocomplete bear [(ngModel)]="beet"></my-autocomplete>
      <my-autocomplete battlestarGalactica [(ngModel)]="battleStarGallactica"></my-autocomplete>
      <button type="submit">Submit</button>
    </form>