2017-03-03 38 views
2

我正在处理一个非常大的项目,我们正在尽可能使模块尽可能模块化。我们有多个Angular应用程序,并且我们已经创建了一个独立的通用UI组件库和一个API工具包,以及这些应用程序中使用的常用服务。将特定服务实例注入到一个父组件的多个实例中

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

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

例如:

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

@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... 
} 

现在我可以定义一个承担实施Autocomplete服务的服务。我可以将熊服务连接到我的自动完成组件,所以我可以选择我的形式的熊种类。

@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... 
} 

到目前为止,这么好。

当我需要构建使用多个自动完成组件的大型表单时,出现了我的问题。我的老板告诉我,在这张表格上需要三个自动填充字段,一个用于熊类物种,一个用于甜菜物种,另一个用于太空堡垒Gallactica角色。我首先想到的是要做到这一点:

我定义的服务实例:

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

然后我更新父模板和供应商:

@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... 
} 

现在我怎么能分出哪个自动完成组件使用哪种服务?

我知道我现在有什么总是使用为AutocompleteService提供的最后一个提供者,因为这是Angular DI框架的工作原理。

我也知道我不能使用多供应商,因为Angular只将它们定义为NG_VALIDATORSNG_ASYNC_VALIDATORS

那么,有没有人有任何想法我可以解决我的问题?我不在乎问题如何得到解决本身,但我仍然需要能够:

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

回答

2

我会移动的供应商一个指令,有选择地加到

@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

+0

感谢这个答案删除供应商!我要把它付诸实施,它不像我所希望的那样优雅。现在,使用我们的库的人有添加步骤创建一个新的指令,并将其添加到模块,并创建新的服务。我很想看到直接在视图中注册提供者的方法 也许将来我们会得到一些很酷的方式来使用多个提供者并直接定义实例在视图上。我知道我们也有viewProviders,但这种情况有点不同。 –

+1

您需要在构造函数中注入子类('BattlestarGalacticaService'),否则DI现在知道注入的内容。你可以做的是{提供:'AutocompleteService',useValue:{bear:new BearService(),beet:new BeetService(), battlestarGalactica:new BattlestarGalacticaService()}}'然后在组件中添加'constructor(@ Inject('AutoCompleteService')private ac:any){} @Input()type:string;'当你需要访问你使用'this.ac [type]'的服务' –

+0

那太棒了,完美。非常感谢Günter。 –

相关问题