【问题标题】:Angular 7 - How to call an API one after oneAngular 7 - 如何一个接一个地调用 API
【发布时间】:2019-03-10 07:20:00
【问题描述】:

加载第一个 API 时,我需要获取 value 并将其传递给第二个 API 以获取详细信息。随着我当前的代码变量变得未定义。这是我的代码:

app.ts

export class DailyEndorsementComponent implements OnInit {

  public selectedShop: string;
  shop: string[];
  shopData: ShopData;


  constructor(private endorsementService: EndorsementService) {}

  ngOnInit() {
    this.getDates();
    this.loadShopList();
  }

  setPayload() {
    return {
      'startDate': this.fromDate,
      'endDate': this.toDate,
      'settlementBank': this.selectedShop,
    }
  }

  getDates(range: any) {
    this.fromDate = this.datePipe.transform(range.fromDate, 'yyyy-MM-dd');
    this.toDate = this.datePipe.transform(range.toDate, 'yyyy-MM-dd');
    this.loadMerchantDailyEndorsement();
  }

  loadShopList() {
    this.endorsementService.shopList()
      .subscribe(response => {
        this.shop = response;
        this.selectedShop = this.banks[0];
      })
  }

  loadMerchantDailyEndorsement() {
      this.endorsementService.getMerchantEndorsement(this.setPayload())
        .subscribe((response: EndorsementResponseInterface) => {
            this.shopData = response;
          }

        }

app.service.ts

export class EndorsementService extends RestService {

  private BASE_URL_MERCHANT: string = '/settlement/merchant/settlement';
  private BASE_URL_CUSTOMER: string = '/settlement/customer/settlement';

  private headers = new HttpHeaders({
    'Content-Type': 'application/json',
    'Authorization': localStorage.getItem('token')
  });

  constructor(protected httpClient: HttpClient,
    protected injector: Injector,
    protected snackBar: MatSnackBar) {
    super(injector);
  }

  getMerchantEndorsement(body: any): Observable < EndorsementResponseInterface > {
    return this.post < EndorsementResponseInterface >
      (this.getFullUrl(`${this.BASE_URL_MERCHANT}/daily/get`), body, {
        headers: this.headers
      });
  }

  shopList(): Observable < string[] > {
    return this.get < string[] >
      (this.getFullUrl(`/settlement/settlementbanks`), {
        headers: this.headers
      });
  }

}

我需要从 loadShopList() 中获取价值,然后调用并将其传递给 loadMerchantDailyEndorsement()。如果我错过了什么,请告诉我。

【问题讨论】:

  • 请简化您的问题,您可以获得更好的答案

标签: angular typescript api asynchronous


【解决方案1】:

为此,您需要将 observable 的 observable 扁平化。

目标是拥有 1 个可观察对象(此处为 this.endorsementService.shopList),这将是 mergeMap 到另一个可观察对象(此处为 this.endorsementService.getMerchantEndorsement

这意味着任何时候shopList observable 都会在流上发出新数据:

  • 您将根据shopList 的当前值创建新的 observable getMerchantEndorsement
  • 将实际的shopList 转换为getMerchantEndorsement 流。

我已经创建了虚拟演示来说明:

// Simple observable to emulate selected shop from list.
const shopDetail$ = of({
  shop: {
    id: 1,
    name: 'My little poney Inc'
  }
}).pipe(delay(100)); // Delay is added to emulate api call delay. is optional

// Factory for remote data : simple function which emulate http request.
// Base on current id parameter, will return observable of marchandize detail.
const getMarchandizeDetail$ = (id: number) => {
  const dummyDb = { 
    '1': {marchandize : ['poney 1', 'poney 2', 'poney 3']},
    '2': {marchandize : ['element 1', 'element 2', 'element 3']},
  };

  return of(dummyDb[id]);
};

// When current shop change...
shopDetail$.pipe(mergeMap(
  info => {
    // We subscribe to http call : getMarchandizeDetail base on the current shop id.
    return getMarchandizeDetail$(info.shop.id)
  }
)).subscribe(console.log);

live demo

【讨论】:

  • 感谢亚尼斯的回复。有没有什么方法可以调用 2 两个 api,一一调用而不延迟?
  • 是的,当然,我添加了延迟显示,第二个 api 调用不会在第一个之前触发。
【解决方案2】:

有几种不同的方法可以实现这一点,但我认为你应该利用 Promises。

export class DailyEndorsementComponent implements OnInit {

  public selectedShop: string;
  shop: string[];
  shopData: ShopData;

  constructor(private endorsementService: EndorsementService) {}

  ngOnInit() {
    this.loadShopList().then(() => {
      this.getDates();
    });
  }

  setPayload() {
    return {
      'startDate': this.fromDate,
      'endDate': this.toDate,
      'settlementBank': this.selectedShop,
    }
  }

  getDates(range: any) {
    this.fromDate = this.datePipe.transform(range.fromDate, 'yyyy-MM-dd');
    this.toDate = this.datePipe.transform(range.toDate, 'yyyy-MM-dd');
    this.loadMerchantDailyEndorsement();
  }

  loadShopList() {
    return new Promise((resolve) => {
        this.endorsementService.shopList()
          .subscribe(response => {
            this.shop = response;
            this.selectedShop = this.banks[0];
            resolve();
          });
    });
  }

  loadMerchantDailyEndorsement() {
      this.endorsementService.getMerchantEndorsement(this.setPayload())
        .subscribe((response: EndorsementResponseInterface) => {
            this.shopData = response;
      }
    }

这似乎是您想要的 - 等到第一个请求完成后再开始第二个请求。


Documentation

【讨论】:

  • 感谢您的回复。什么是 resolve() ?我们需要 declatre resolve() 吗?
  • @SKL resolve 是我们告诉 Promise 已经完成的方式。它表示作为 Promise 本身一部分的回调(具体来说,它是创建 Promise 时声明的匿名函数的第一个参数)
  • 它现在可以工作,但是当更改日期时 loadShopList() 会再次加载。它应该只加载一次。如果我更改日期,它应该只加载 loadMerchantDailyEndorsement()
【解决方案3】:

您可以使用 rxjs 的 mergeMap 运算符来做到这一点。

Angular 4.3+(使用 HttpClientModule)和 RxJS 6+

getMerchantEndorsement(body): Observable<EndorsementResponseInterface> {
    this.http.post(this.getFullUrl(`/settlement/settlementbanks`, {headers})
      .pipe(mergeMap(character => this.http.get(character.shopId)));
 }

供参考,可以查看here

【讨论】:

    猜你喜欢
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 2020-09-17
    • 2020-11-15
    • 1970-01-01
    相关资源
    最近更新 更多