简体   繁体   中英

Angular 4 call service method with data from previous service method call

I'm currently learning Angular while building a site that's meant to present results from fencing matches in real time as they are input from a desktop competition manager application.

I'm having problems with how to "chain" observable subscriptions. I've made it work but I'm pretty sure there has to be a more elegant solution.

When I route to a match i pass in the Id to get the match, like

getMatch(id: number){
    this._matchService.getMatch(id).subscribe
    (
      match => this.match = match,
      error => this.errorMessage = <any>error
    )

The problem arises from the fact that the match object contains the Ids from the pool, competition and fencers in the match. So I need to use the response from getMatch to get competition, fencers and pool.

I'm currently doing it like this, but it doesn't feel very good. I'm considering rebuilding the API do deliver all needed data in one call, but I'd like to make this work.

getMatch(id: number){
    this._matchService.getMatch(id).subscribe(
      match => {
        this.match = match;

        this._fencerService.getFencer(match.blueFencerId).subscribe(
          bf => this.blueFencer = bf,
          error => this.errorMessage = <any>error
        );

        this._fencerService.getFencer(match.redFencerId).subscribe(
          rf => this.redFencer = rf,
          error => this.errorMessage = <any>error
        );

        this._poolService.getPool(match.poolId).subscribe(
          pool => {
            this.pool = pool.name;

      this._competitionService.getCompetition(pool.competitionId).subscribe(
              comp => this.competitionName = comp.name
            )
          },
          error => this.errorMessage = <any>error
        )
      },
      error => this.errorMessage = <any>error
    )
  }

Also, this is what the matchService method looks like, with is the recurring pattern for the other services aswell:

getMatch(id: number): Observable<IMatch>{
  return this.getPopulatedMatches().map((matches: IMatch[]) => 
     matches.find(p => p.id === id));
}

I'm not sure if I'm doing something fundamentally wrong in my design, but I'd like to atleast split all the service calls into separate methods, somthing like.

ngOnInit() {
   getMatch();
   getPool(match.poolId);
   this.redFencer = getFencer(match.redFencerId);
   this.blueFencer = getFencer(match.blueFencerId);
}

Nested .subscribe() blocks are an anti-pattern.

Try this:

For rxjs > 5.5.2:

  getMatch(id: number) {
    const match$ = this._matchService.getMatch();

    const blueFencer$ = match$.pipe(
      flatMap(({ blueFencerId }) => this._matchService.getFencer(blueFencerId)),
    );

    const redFencer$ = match$.pipe(
      flatMap(({ redFencerId }) => this._matchService.getFencer(redFencerId)),
    );

    const pool$ = match$.pipe(
      flatMap(({ poolId }) => this._poolService.getPool(poolId)),
    );

    const competition$ = pool$.pipe(
      flatMap(({ competitionId }) =>
        this._competitionService.getCompetition(competitionId),
      ),
    );

    return { match$, blueFencer$, redFencer$, pool$, competition$ };
  }

For rxjs < 5.5.2:

  getMatch(id: number) {
    const match$ = this._matchService.getMatch();

    const blueFencer$ = match$
      .flatMap(({ blueFencerId }) => this._matchService.getFencer(blueFencerId));

    const redFencer$ = match$
      .flatMap(({ redFencerId }) => this._matchService.getFencer(redFencerId));

    const pool$ = match$
      .flatMap(({ poolId }) => this._poolService.getPool(poolId));

    const competition$ = pool$
      .flatMap(({ competitionId }) =>
        this._competitionService.getCompetition(competitionId));

    return { match$, blueFencer$, redFencer$, pool$, competition$ };
  }

Then in your component you can subscribe to each individual stream and do whatever you want.

Edit: If you want the streams to be created inside the MatchService :

export class MatchService {
  match$ = this.getMatch();

  blueFencer$ = this.match$.pipe(
    flatMap(({ blueFencerId }) => this.getFencer(blueFencerId)),
  );

  redFencer$ = this.match$.pipe(
    flatMap(({ redFencerId }) => this.getFencer(redFencerId)),
  );

  pool$ = this.match$.pipe(
    flatMap(({ poolId }) => this._poolService.getPool(poolId)),
  );

  competition$ = this.pool$.pipe(
    flatMap(({ competitionId }) =>
      this._competitionService.getCompetition(competitionId),
    ),
  );

  constructor(
    private _poolService: PoolService,
    private _competitionService: CompetitionService,
  ) {}

  getMatch(id: number): Observable<any> {
    // ...
  }

  getFencer(id: number): Observable<any> {
    // ...
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM