Problem: Within your Observable pipe, you want to make a bunch of API calls to populate more info for the emitted value, and combine everything back into a single observable.
In this example, we have an Angular component which has a method for fetching a list of people from an API and populates the value of their addresses before returning the list.
interface Person {
name: string;
addressId: string;
addressText: string;
}
interface PersonService {
// fetches a list of Person objects with name and addressId, but with addressText = null
getAllPeople(): Observable<Person[]>;
// fetches the addressText for a given addressId
getAddressText(addressId: string): Observable<string>;
}
export class PersonComponent {
constructor(private personService: PersonService) { }
populateAddress(p: Person): Observable<Person> {
return this.personService.getAddressText(p.addressId).pipe(
map(addrText => ({...p, addressText: addrText}))
);
}
getAllPeopleWithTheirAddressesPopulated(): Observable<Person[]> {
return this.personService.getAllPeople().pipe(
concatMap((listOfPeople: Person[]) => {
const arrayOfObservables: Observable<Person>[] = listOfPeople.map(p => this.populateAddress(p));
return arrayOfObservables;
}),
concatAll(),
toArray()
);
}
}
Note that we get an array of Person
objects from the service call, and we make an API call for each person, update the value of their addressText
, and then they all get combined back into an array of Person
objects, and the resulting observable can finally emit the Person
objects as an array once all of their address texts are populated.
Note: this uses concatAll()
to have the address-population requests execute one-at-a-time, but mergeAll()
can be used to have them execute in parallel.
Links to the operators:
Versions being used:
- typescript: 4.0.3
- @angular/core: 10.1.5
- ngrx: 10.0.1
- rxjs: 6.6.3
Leave a Reply