I'm trying to get a list of courses from one API endpoint and get the assignments associated with each course from another. I am using concatMap to merge the observables but it isn't working as I expected. Instead of emitting the assignments for each course, it's emitting observable objects.
function getCourses() {
return fromFetch("https://canvas.instructure.com/api/v1/courses", {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization:
"Bearer 2~WSiTptufbMw24WKz0gWbb34tSabWEbKKVR5bQY3NEL2b5pd7Ah8OEzM8P64FS4ta",
},
selector: responseAsJson,
});
}
function getAssignments(id: number) {
return fromFetch(
`https://canvas.instructure.com/api/v1/courses/${id}/assignments?bucket=upcoming`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization:
"Bearer ***",
},
selector: responseAsJson,
}
);
}
const assignments = getCourses()
.pipe(
map((response) => response.map((r: any) => getAssignments(r.id))),
concatAll(),
)
assignments.pipe(tap(r => console.log(r))).subscribe();
/*
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
Observable { _subscribe: [Function (anonymous)] }
*/
CodePudding user response:
I was able to make it work by using switchMap
and forkJoin
const assignments = getCourses().pipe(
// unsubscribe from the outer observable after the inner emits
switchMap((response) =>
// convert a collection of observables into an observable collection
forkJoin(response.map((r: any) => getAssignments(r.id)))
)
);
CodePudding user response:
This is happening because your map()
operator returns Promise[]
which means it return an array of Promises. concatAll()
takes the array as an observable-like input, iterates its values and emits them as separate emissions. In other words, concatAll()
will "subscribe" to the array and not Observables inside the array.
So you need to flatten the array into individual emission for example with mergeMap()
:
const assignments = getCourses()
.pipe(
mergeMap((response) => response.map((r: any) => getAssignments(r.id))),
concatAll(),
)
... or you could also use two cocantAll()
but that will look really weird:
const assignments = getCourses()
.pipe(
map((response) => response.map((r: any) => getAssignments(r.id))),
concatAll(),
concatAll(),
)
The first concatAll
will flatten the array and the second one will subscribe to individual Observables.
... or you could just merge all inner Observables
const assignments = getCourses()
.pipe(
mergeMap((response) => merge(...response.map((r: any) => getAssignments(r.id)))),
)