TypeScript: последовательно отправлять несколько HTTP-запросов Angular2

Я отправляю запрос в веб-API и снова отправляю другой запрос в тот же API на основе возвращаемого значения из первого возврата API. Затем я пытаюсь сопоставить компоненты веб-API с классом, но метод сопоставления не ожидает возврата второго вызова. Пожалуйста, помогите, как я могу сопоставить после успешного возврата обоих вызовов?

Ниже мой код

getOrderDetail(orderId: string): Observable<OrderHeader> {

return this.svc.wCWebClientServiceGetOrderDetails({ _orderId: orderId })
  .do(order => {
    order.SalesLines.forEach(saleLine => {
      if (saleLine.RelatedOrders !== undefined && saleLine.RelatedOrders.length > 0) {
        saleLine.RelatedOrders.forEach(relatedOrder => {
          this.svc.wCWebClientServiceGetOrderDetails({ _orderId: relatedOrder.TransId })
            .subscribe(relOrder => {
              //debugger;
              relOrder.SalesLines.forEach(relLine => {
                order.SalesLines.push(relLine);
              });
            })
        });
      }
    })
  })
  //.do(order => console.log('Received order', order))
  .map(order => this.transformOrder(order));

}


person Zahid Hamid    schedule 02.03.2018    source источник


Ответы (3)


Вы можете использовать оператор RxJs под названием MergeMap, также известный как flatMap, для сопоставления/перебора значений Observable.

import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { mergeMap } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: 'app/app.component.html'
})
export class AppComponent {
  homeworld: Observable<{}>;
  constructor(private http: HttpClient) { }

  ngOnInit() {
    this.homeworld = this.http.get('/api/people/1').pipe(
      mergeMap(character => this.http.get(character.homeworld))
    );
  }
}

Итак, в нашем примере, когда мы получаем родной мир, мы возвращаем Observable внутри потока Observable нашего персонажа. Это создает вложенный Observable в Observable. Оператор mergeMap помогает нам, подписываясь и извлекая значение из внутреннего Observable и передавая его обратно в родительский поток.

Для получения дополнительной информации вы можете проверить этот блог: Множественные HTTP-запросы Angular с RxJS

person Isaac Obella    schedule 02.03.2018

Этот оператор flatMap в сочетании с forkJoin должен помочь.

getOrderDetail(orderId: string): Observable<OrderHeader> {
    return this.svc.wCWebClientServiceGetOrderDetails({ _orderId: orderId })
    .flatMap(order => {
        order.SalesLines.forEach(saleLine => {
            let observables:Array<Observable<any>> = [];

            if ( /* Your predicate */ ) {
                saleLine.RelatedOrders.forEach(relatedOrder => {
                    observables.push(this.svc.wCWebClientServiceGetOrderDetails({ _orderId: relatedOrder.TransId }));
                });
            }

            // Perform the calls to retrieve order details asynchronously
            return Observable.forkJoin(observables).map((results:Array<any>) => {
                 results.forEach(relOrder => {
                    relOrder.SalesLines.forEach(relLine => {
                        order.SalesLines.push(relLine);
                    });
                 });
            });
        });
    })
    // Perform your final transformation on the results
    .map(order => this.transformOrder(order));
}
person JeanPaul A.    schedule 02.03.2018
comment
Я получаю сообщение об ошибке, когда использую .flatMap(order =› {. Сообщение об ошибке: аргумент типа '(order: WCRentalOrder) =› void' не может быть назначен параметру типа '(value: WCRentalOrder, index: number) = › ObservableInput‹{}›». - person Zahid Hamid; 02.03.2018

Я считаю, что вам нужен метод expand.

Вот мой собственный пример:

VA7B(params: VA7B, accountList: any, balanceSequenceList: string[], docNumber: string) {
        const sequenceList = [...balanceSequenceList];

        // Função que define o código do balanço financeiro para a conta atual
        const setBalanceCode = () => {
            params.balance.accountNumberRecalled = StringFormat.padLeft('', 7);
            params.balance.demoAccountNumber = sequenceList[0];
            sequenceList.shift();
        };

        const total = accountList.reduce((acc: any, curr: any) => acc + curr.length, 0);

        // Função que realiza a requisição RESTFUL ao servidor.
        const post = (account: string): Observable<VA7BResponse> => {
            const payload: PayLoad<VA7B> = new PayLoad<VA7B>();

            params.balance.document.number = docNumber;
            payload.obj = params;
            payload.obj.balance.accountData = account;

            payload.url = 'url_VA7B';
            payload.result = VA7BResponse;
            payload.hasCripto = this.hasCripto;
            payload.criptoFields = [{ balance: Object, document: Object, number: String }];
            payload.decriptoFields = [];

            return this.baseService.POST<VA7B>(payload);
        };

        setBalanceCode();

        // O método 'expand' é responsável por chamar a função REST recursivamente, caso ainda hajam contas a ser importadas.
        return post(accountList[0][0])
            .expand((res: VA7BResponse) => {
                params.balance.accountNumberRecalled = accountList[0][0].substr(-25, 7);
                accountList[0].shift(); // Após ser importado, o primeiro item é removido da lista.

                // Caso a primeira lista esteja vazia, remove a lista e passa para o próximo demonstrativo.
                if (accountList[0].length === 0) {
                    accountList.shift();
                    setBalanceCode();

                    // Caso não hajam mais balanços, encerra o processo e retorna.
                    if (accountList.length === 0) {
                        return Observable.empty();
                    }
                }

                return post(accountList[0][0]);
            }).take(total);
    }
person LordAlpaca    schedule 02.03.2018