Отправить запрос GET непосредственно после того, как объект из предыдущего запроса GET доступен? - PullRequest
1 голос
/ 06 ноября 2019

При инициализации MatchComponent я хочу сделать 1. Получить запрос на получение объекта Match (объект Match имеет идентификатор playerID члена) 2. Получить запрос на получение Players (на основе ID игрока в объекте Match)

Из-за асинхронной связи мой код ниже не работает. Как я могу справиться с этим?

match.component.ts

@Component({
  selector: 'app-match',
  templateUrl: './match.component.html',
  styleUrls: ['./match.component.css']
})
export class MatchComponent implements OnInit {

  match: Match;
  players: Player[];

  constructor(private matchService: MatchService,
              private playerService: PlayerService,
              private route: ActivatedRoute) { }

  ngOnInit() {
    this.loadData();
  }

  loadData(): void {
    const matchID = +this.route.snapshot.paramMap.get('id');
    this.getMatchByUniqueID(matchID); // first get request
    this.match.playerIDs.forEach(id => {
      this.getPlayerByUniqueID(id); // get requests that can only work when the match object is set correctly
    });
  }

  // ---------------------------------------------------------
  // HTTP ----------------------------------------------------
  // ---------------------------------------------------------
  getMatchByUniqueID(id: number): void {
    this.matchService.getMatch(id)
      .subscribe(match => {
        if (match.status === 'SUCCESS') {
          this.match = Object.setPrototypeOf(match.data, Match.prototype);
        }
      });
  }

  getPlayerByUniqueID(id: number): void {
    this.playerService.getPlayer(id)
      .subscribe(player => {
        if (player.status === 'SUCCESS') {
          this.players.push(Object.setPrototypeOf(player.data, Player.prototype));
        }
      });
  }

  updateMatch(match: Match): void {
    console.log('update');
    this.matchService.updateMatch(match)
      .subscribe(() => this.match);
  }

}

match.ts

export class Match {
    //...
    playerIDs: number[]; /// IDs of players playing this match
    //...
}

match.service.ts

import { Match } from './match';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpHandler } from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';

import { HttpResponseType } from './http.response';

@Injectable({
  providedIn: 'root'
})
export class MatchService {

  private matchesURL = 'http://localhost:8080/matches';

  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };

  constructor(private http: HttpClient) { }

  getMatch(id: number): Observable<HttpResponseType<Match>> {
    const url = `${this.matchesURL}/${id}`;
    return this.http.get<HttpResponseType<Match>>(url)
      .pipe(
        // tap(_ => this.log(`fetched hero id=${id}`)),
        catchError(this.handleError<HttpResponseType<Match>>(`getUser id=${id}`))
      );
  }

  /** PUT: update the match on the server */
  updateMatch(match: Match): Observable<any> {
    return this.http.put(this.matchesURL + '/' + match.uniqueID, match, this.httpOptions).pipe(
      // tap(_ => this.log(`updated user id=${user.id}`)),
      catchError(this.handleError<Match>('updateMatch'))
    );
  }

  // ...

Ответы [ 3 ]

1 голос
/ 06 ноября 2019

Если я правильно понимаю ваши потребности, вы хотите сделать запрос 2, используя данные из запроса 1.

Это легко сделать с помощью оператора RxJs switchMap.

Все, что вам нужно, это

// First you pipe to the source observable which GETs the data you need
this.firstRequest$.pipe(
  // you may apply a filter() pipe which will pass data forth if it returns true
  // like filter(res => res.status === 'SUCCESS')

  // Then you call method which receives some needed data for second request
  // and returns observable of 2nd request
  switchMap(res => this.secondRequest(res.id))
).subscribe(res => {
  // process the result of 2nd request
});

Вот небольшой пример

https://stackblitz.com/edit/rxjs-47hmp1?devtoolsheight=60

import { of } from 'rxjs'; 
import { map, filter, switchMap } from 'rxjs/operators';

// Here we get a source observable
function getFirstObservable() {
  return of({
    data: {
      id: 3
    },
    status: 'SUCCESS',
  });
}

// This is a second source which requires some data to receive first
function getSecondObservable(id: number) {
  return of('I was called with id ' + id);
}

getFirstObservable().pipe(
  // filter allows emmited values to pass only when resolves to true
  filter(response => response.status === 'SUCCESS'),
  // Allows us to subsribe to the observabe returned by callback when
  // source observable emits value
  switchMap(response => getSecondObservable(response.data.id))
).subscribe(result => {
  console.log(result);
  // handle result

  // here goes the result of a second observable when and only when second observable
  // emmits value
}, err => {
  // handle error logic
})
0 голосов
/ 06 ноября 2019

Вам нужно изменить методы loadData и getMatchByUniqueID в match.component.ts, чтобы получить правильный ответ. Вы должны вызвать метод getPlayerByUniqueID после получения всех данных из вашего первого вызова API.

loadData(): void {
  const matchID = +this.route.snapshot.paramMap.get('id');
  this.getMatchByUniqueID(matchID);          
}

getMatchByUniqueID(id: number): void {
  this.matchService.getMatch(id)
    .subscribe(match => {
      if (match.status === 'SUCCESS') {
        this.match = Object.setPrototypeOf(match.data, Match.prototype);

        // call the playerByUniqueId from here
        this.match.playerIDs.forEach(id => {
          this.getPlayerByUniqueID(id);
        });
      }
    });
}
0 голосов
/ 06 ноября 2019

Последующие зависимые запросы выполняются только после завершения первого вызова - для этого используется Observable, возвращаемый запросом http. Это позволяет вам связывать ваши запросы в нужном порядке.

  loadData(): void {
    const matchID = +this.route.snapshot.paramMap.get('id');
    this.getMatchByUniqueID(matchID).subscribe(match => { //subscribe to the http observable
        if (match.status === 'SUCCESS') {
          this.match = Object.setPrototypeOf(match.data, Match.prototype);
        }
        this.match.playerIDs.forEach(id => {
        this.getPlayerByUniqueID(id); // get requests that can only work when the match object is set correctly
      });
  }

  // ---------------------------------------------------------
  // HTTP ----------------------------------------------------
  // ---------------------------------------------------------
  getMatchByUniqueID(id: number): Observable { // <-- note we are returning the observable here
    return this.matchService.getMatch(id);
  }

  getPlayerByUniqueID(id: number): void {
    this.playerService.getPlayer(id)
      .subscribe(player => {
        if (player.status === 'SUCCESS') {
          this.players.push(Object.setPrototypeOf(player.data, Player.prototype));
        }
      });
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...