Seb_Lz
Seb_Lz

Reputation: 133

Provide Component with a service instance that depends on component @Input

I have a Master-Detail component that contains children grid and detail components.

Based on an @Input entityType parameter, the grid and the detail components will need to initialize their specific dataService property to DataService1, DataService2, ... or DataServiceN. What would be the best way to achieve this ? I see various possibilities:

  1. Create an @Injectable factory service with a createServiceInstance(entityType) method:

    public createService(type: EntityType): any {
        switch (type) {
            case EntityType.Type1:
                return new DataService1();
            case EntityType.Type2:
                return new DataService2();
            case EntityType.TypeN:
                return new DataServiceN();
        }
    }
    

    Then inject that service into Master-Detail Component constructor and assign do

    this.dataService = this.factoryService.createService(entityType)
    

    in ngOnInit / ngOnChanges. We then add @Input dataService to grid and detail components and bind it to masterDetail dataService property.

  2. Use an Injector service in detail/grid components, have the providers property of @Component decorator set to [DataService1, DataService2, ..., DataServiceN] and then in ngOnInit do

    this.dataService = this.injector.get(getDataService(entityType))
    

    with

    export function getDataService(entityType: string): any {
        switch (type) {
            case EntityType.Type1:
                return DataService1;
            case EntityType.Type2:
                return DataService2;
            case EntityType.TypeN:
                return DataServiceN;
        }
    }
    
  3. Use a mix of 1) and 2) : provide ServiceFactory with [DataService1, DataService2, ..., DataServiceN] in its constructor, then in detail/grid component ngOnInit do

    this.dataService = this.factoryService.getDataService(entityType)
    

    with

    getDataService(entityType: string): any {
        switch (type) {
            case EntityType.Type1:
                return this.dataService1;
            case EntityType.Type2:
                return this.dataService2;
            case EntityType.TypeN:
                return this.dataServiceN;
        }
    }
    
  4. Another solution?

I'm using Angular 4 with TypeScript.

Upvotes: 2

Views: 997

Answers (1)

BRNTZN
BRNTZN

Reputation: 564

I had a similar issue and found following working solution:

I have interface FileService:

export interface FileService {
    downloadDocument(ownerId: string, fileId: string): Observable<any>;
}

Then I have all the possible services implement this interface. In the child component I have an input:

export class SomeTestComponentComponent {

    @Input() fileService: FileService;

    download() {
        console.log(this.fileService);
        // this.fileService.download(...);
    }

}

Now in the parent component I can decide which service should be used in this instance of the component:

<app-some-test-component [fileService]="fileServiceImpl" ></app-some-test-component>

This approach means you don't supply a string to decide which service to use but rather the service itself.

Upvotes: 0

Related Questions