Angular 8 + RxJS以及flatMap和forkJoin结合使用的问题

时间:2019-12-30 12:56:27

标签: javascript angular angular8 rxjs6 flatmap

我试图使多个http请求依赖于上一个请求,并将返回的数据组合到最终结果对象中。

我有两个端点:

  1. /properties会给我类似的信息:

    { 
      "data":[ 
        { 
          "id":1,
          "name":"Property 1",
          "units":[ 
            { 
              "id":1,
              "name":"Property 1 - Unit 1",
            },
            { 
              "id":2,
              "name":"Property 1 - Unit 2",
            },
          ],
        },
        { 
          "id":2,
          "name":"Property 2",
          "units":[ 
            { 
              "id":3,
              "name":"Property 2 - Unit 3",
            }
          ]
        }
      ]
    }```
    
  2. /properties/${property.id}/units/${unit.id}/bookings会向我返回一些与先前请求相关的对象,例如:

    {
      "data": {
        "id": 1
      },
      "meta": {
        "count": 5
      }
    }
    

我想要实现的是将这两个响应在单个单元级别上组合为一个。 像这样:

{
  [
    ...

    { 
      "id":2,
      "name":"Property 2",
      "units":[ 
        { 
          "id":3,
          "name":"Property 2 - Unit 3",
          "bookings_meta": {
            "count":5
          }
        }
      ]
    }
  ]
}

我创建了类似的东西,并且效果很好:


    list(): Observable<any> {
      return this.http.get('/properties').pipe(
        map((results: any) => results.data),

        flatMap((properties: any[]) => {
          if (properties.length > 0) {
            return forkJoin(properties.map((property: any) => {
              return forkJoin(property.units.map((unit: any) => {
                return this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`).pipe(
                  map(({ meta }) => {
                    unit.bookings_meta = meta
                    return unit;
                  })
                )
              }))
            }))
          }
          return of([]);
        })
      );
    }


    this.list().subscribe(response => {
        console.log(response)
    })

但是我认为这不是100%正确的解决方案。 我感觉forkJoin太多了,也许不应该flatMap那里呢?

单元之间的预订请求应独立。

有人对如何改进上面的代码有想法吗?

2 个答案:

答案 0 :(得分:1)

forkJoin将在所有调用结束后返回数据并返回结果

const combined = Observable.forkJoin(
  this.http.get('https:// ...properties').map((res: Response) => res.json()),
  this.http.get('https:// ..properties/${property.id}/units/${unit.id}/bookings')
      .map((res: Response) => res.json())
)

combined.subscribe(latestValues => {
    console.log( "latestValues" , latestValues );
});

更新:

可以使用flatMap运算符将一个Observable发出的项目转换为另一个Observable。它创建一个内部Observable并将其结果平坦到外部流。

list(): Observable<any> {
    return this.http.get('/properties')
        .flatMap(properties => properties.map(p => p.units.map(unit => 
        {
            this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`)   
        })))
        .subscribe(res => { // some actions });
}

答案 1 :(得分:0)

最后我找到了一个可观察到的地狱的答案:)

list(): Observable<any> {
  return this.http.get('/properties').pipe(
    map((results: any) => results.data),

    mergeMap((properties: any[]) => forkJoin(
      properties.map((property: any) => forkJoin(
        property.units.map((unit: any) => this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`).pipe(
          map(({ meta }) => ({ ...unit, meta }))
        ))
      )).pipe(map((units: any[]) => ({ ...property, units }))))
    ))
  );
}

干杯!