问题
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
getSomeInfoand store the result, which should be an array, into thespecialPropproperty - then, for each entry in the
specialProparray, you want to call thegetSomeOtherInfomethod, fetch some more data and store it into a property calledanotherSpecialProp
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