Angular RxJs level up during looping an array

不羁的心 提交于 2019-12-11 04:56:02

问题


I have an http get request to get an array like

[
  {name: 'name1', id: 1, specialProp: [] },
  {name: 'name2', id: 2, specialProp: [] }
]

I need to get each of array items, take an id and send a request to server to get some information. The results should be written into the property specialProp. After that I need to take the array of the prop specialProp and for each item get some data, put it into anotherSpecialProp. In the end I should have the final array like

[
  {name: 'name1', id: 1, specialProp: [
    {name: 'c', anotherSpecialProp: []}, 
    {name: 'd', anotherSpecialProp: []}
  ]},

  {name: 'name2', id: 2, specialProp: [
    {name: 'a', anotherSpecialProp: []},
    {name: 'b', anotherSpecialProp: []}
  ]}
]

I have the code:

this.http.get(url)
  .pipe(
    switchMap((mainItemArr: any) => from(mainItemArr)),
    mergeMap((mainItem: any): any => {
      return this.getSomeInfo(mainItem.Id) //another http get request
        .pipe(
          map((data: any): any => {
            return Object.assign(mainItem, { specialProp: data })
          }),
          switchMap((mainItemArr: any): any => from(mainItemArr.specialProp)),
          concatMap((item: any): any => {
            return this.getSomeOtherInfo(item.Id) // one more http get request
              .pipe(
                map((data: any): any => Object.assign({}, task, { anotherSpecialProp: data }))
              )
          }),
        )
    })
  )

So in subscribe I receive just the items, not the whole mainItemArr. Could anyone please assist me with the issue?:)


回答1:


The main trick is to use map to merge scoped property with request result.

Here's a rough example how to achieve this for the first level (specialProp):

this.http.get(url).pipe(
  mergeMap(mainItemArr => {
    // forkJoin will wait for each request to complete
    return forkJoin(
      // make a subsequent request for each item in mainItemArr
      mainItemArr.map(mainItem => {
        return this.getSomeInfo(mainItem.Id).pipe(
          // merge getSomeInfo result with the mainItem
          map(someInfo => {
            return {
              ...mainItem,
              specialProp: someInfo
            };
          })
        )
      })
    )
  })
)

For the anotherSpecialProp requests — you'll need to go one level deeper.

In a real world application I'd suggest splitting those subsequent calls into separate functions/methods.

NOTE:

You don't need turning array into Observable:

mergeMap(mainArray => mainArray)

Instead you might keep it in the JS scope and do subsequent requests in the mergeMap, e.g.:

mergeMap(mainArray => {
  // making sub requests here
})

Using mergeMap to turn array into Observable should work too, though it might be more confusing when diving 1 level deeper, imho. Anyway, the map does the main trick.

Hope this helps




回答2:


If I get it right, what you need to do is the following:

  • fetch the initial array from back end
  • for each element of the array, call getSomeInfo and store the result, which should be an array, into the specialProp property
  • then, for each entry in the specialProp array, you want to call the getSomeOtherInfo method, fetch some more data and store it into a property called anotherSpecialProp

If all this is true, then you can try something along these lines

getArray()
.pipe(
    mergeMap(mainArray => mainArray),  // unwind the array received
    switchMap(mainItem => getSomeInfo(mainItem.id) // fetch the first set of info from backend
        .pipe(
            tap(someInfo => {
                mainItem['specialProp'] = someInfo; // wrote someInfo into specialProp property
            }),
            mergeMap(specialProps => specialProps), // unwind the array of specialProps
            switchMap(specialProp => getSomeOtherInfo(specialProp.name) // for each specialProp fetch the additional data
                .pipe(
                    tap(someOtherInfo => {
                        specialProp['anotherSpecialProp'] = someOtherInfo // store additional data into anotherSpecialProp property
                    })
                )
            ),
            toArray(), // rewind the array of  specialProps and return it
            map(() => mainItem)
        )
    ),
    toArray() // rewind the array of mainItems and return it
)

The thing you may want to notice is the use of mergeMap with an Array, e.g. mergeMap(mainArray => mainArray).

mergeMap accepts as input a function which returns an ObservableInput. An Array is an ObservableInput which emits all of its items synchronously before completing. So, passing a function which returns an Array to mergeMap means to emit all the elements of the Array.

You can find an example of the above example here



来源:https://stackoverflow.com/questions/55340585/angular-rxjs-level-up-during-looping-an-array

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!