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

Col*_*ord 5 service components dependency-injection angularjs angular

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

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

对于一个实例来说,这种实现很容易。我在我的自动完成组件的构造函数中注入了自动完成服务,然后在父组件上提供它。这使我可以在使用服务时灵活地更改服务的实现细节,同时仍然能够创建与定义的接口一起工作的可重用组件。

例如:

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

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

  constructor(http: Http) {}

  fetch(): any {
    return this.http.get(this.url);
  }
}
Run Code Online (Sandbox Code Playgroud)

自动完成组件实现:

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

  getData() {
    return this.autocompleteService.fetch();
  }
  ...autocomplete logic...
}
Run Code Online (Sandbox Code Playgroud)

现在我可以定义一个实现自动完成服务的熊服务。我可以将熊服务连接到我的自动完成组件,这样我就可以在我的表单中选择熊的种类。

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

  constructor(http: Http){}

  fetch() {
    return this.http.get(this.url);
  }
}
Run Code Online (Sandbox Code Playgroud)

接下来,我定义了使用我的自动完成组件并提供熊服务来获取我的熊种类数据的父级。

@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...
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。

当我需要构建一个使用多个自动完成组件的大型表单时,我的问题就出现了。我的老板告诉我,我需要在此表单上使用三个自动完成字段,一个用于熊类,一个用于甜菜类,一个用于太空堡垒卡拉狄加角色。我的第一个想法是这样做:

我定义了服务实例:

@Injectable()
export class BeetsService {
  public url: string = 'beets-url';

  constructor(http: Http){}

  fetch() {
    return this.http.get(this.url);
  }
}

@Injectable()
export class BattleStarGallacticaService {
  public url: string = 'battlestar-gallactica';
  constructor(http: Http){}

  fetch() {
    return this.http.get(this.url);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我更新父模板和提供者:

@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...
}
Run Code Online (Sandbox Code Playgroud)

现在我怎么知道哪个自动完成组件使用哪个服务?

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

我也知道我不能在这方面使用多提供者,因为 Angular 只为NG_VALIDATORS和定义了它们NG_ASYNC_VALIDATORS

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

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

Gün*_*uer 3

我会将提供商转移到选择性应用的指令

@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{}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它

<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>
Run Code Online (Sandbox Code Playgroud)

并将提供商从MyFormComponent