POV
POV

Reputation: 12005

How to use service inside another?

I have service that performs requests to server:

export class ExportDictionaryApiService {
  constructor(private http: HttpClient) {}
  public perform(): Observable<any> {}
}

There is another class factory:

export class ExportFactory {
  public static createConcreteExcel(type: string) {
    switch (type) {
      case EReferenciesTypes.DictionaryType:
        return new ExportDictionary();
    }
  }
}

And concrete class that is returned by factory:

export class ExportDictionary implements IExport {
  export(parameters: any) {
     this.apiService
      .perform().subscribe((response: IResponseDict) => {});
  }
}

Using is:

ExportFactory.createConcreteExcel('full').export([...parameters]);

Problem is:

Concrete class should use concrete apiService, and now there is no ready object apiService inside class ExportDictionary

How to pass it in concrete class? I need return ready instance with all dependencies inside!

Certainly I can inject ready object in method:

ExportFactory.createConcreteExcel('full').export([...parameters], injectedApiService);

But I dont know injctedApiService uNtil I dont create concrete Factory.

Also I can not create object inside:

export(parameters: any) {
       new ExportDictionaryApiService()
          .perform().subscribe((response: IResponseDict) => {});
 }

Because ExportDictionaryApiService requires dependency HttpClient

Upvotes: 3

Views: 97

Answers (1)

Reza
Reza

Reputation: 19843

See this working example https://stackblitz.com/edit/angular-service-factory

p.s you can change the string to enum

Explanation

You need a factory as below

@Injectable()
export class ExportFactoryService {

 constructor(
    @Inject('Export') private services: Array<IExport>
  ) { }

  create(type: string): IExport {
    return this.services.find(s => s.getType() === type);
  }

}

an interface for your service

export interface IExport {
   getType(): string; // this can be enum as well

   export(parameters: any):any;
}

and your service implementation, I implemented two services

@Injectable()
export class ExportDictionaryService implements IExport {

  constructor() { }

  getType(): string {
    return 'dictionary';
  }

  export(parameters: any):any {
    console.log('ExportDictionaryService.export')
  }

}

and most important part, provide multiple services in app.module

  providers: [

    ExportFactoryService,
    { provide: 'Export', useClass: ExportDictionaryService, multi: true },
    { provide: 'Export', useClass: ExportJsonService, multi: true }
  ]

and this is how you get an instance of your service

  constructor(private exportFactoryService: ExportFactoryService) {}

  create() {
    const exporter = this.exportFactoryService.create('dictionary');
    exporter.export('full');
  }

and this approach is Open-Closed, you can extent it by adding new services, and you don't need to modify existing codes, and there is not if/else, or switch/case statement , no static class, and it's unit testable, you can inject whatever is needed in each exporter services

Upvotes: 4

Related Questions