Reputation: 380
So, I'm new to RxJS and was playing around with Angular 5, wondering how to accomplish the following:
Let's asume we have a form. When the page loads, we need 3 select to be populated with data from the server, so we have 3 Observables to accomplish this.
Now, we also have an observable for when the route params change (in that case, we have to get the requested record and populate the form):
// These 3 guys will get the required data for each select
this.countryService.getAll().subscribe(countries => {
this.countries = countries;
});
this.categoryService.getAll().subscribe(categories => {
this.categories = categories;
});
this.sectorService.getAll().subscribe(sectors => {
this.sectors = sectors;
});
// And this is for the change in url
this.route.paramMap.subscribe(params => {
this.formDisabled = true;
const id = params.get('id');
// We get the resource based on the id param
this.service.get(id).subscribe(contact => {
this.contact = contact;
this.form.reset(this.contact);
this.formDisabled = false;
}
});
Now, I need the callback of this.service.get(id).subscribe()
to execute only after the 3 selects have been populated, i.e. when their respective callbacks have completed, otherwise we may end up trying to do things with the form when it's not completely built. I would like it to remain requesting the resource in parallel to the other 3 requests, but execute the callback (reset the form with it) only after the other 3 are completely done.
Upvotes: 4
Views: 6955
Reputation: 8478
It often helps if you try to layout the steps on what you want to achieve, instead of how you want to achieve. This trains you to think of in a more "reactive" manner.
Get the 3 dropdowns. This can be done in parallel
Retrieve the route params
Do the rest:
Assign the values of contact.
Reset the form
Renable the form
Once you layout the steps, coding them as observables will be quite trivial:
//Step 1: Call the dropdownsService in parallel
Observable.forkJoin([
this.countryService.getAll(),
this.categoryService.getAll(),
this.sectorService.getAll()
])
.switchMap(([countries, categories, sectors]) => {
//Assign the dropdown values
this.countries = countries;
this.categories = categories;
this.sectors = sectors;
//Step 2: Retrieve the route params
return this.route.paramMap;
})
.switchMap(({id}) => {
//disable the form
this.formDisabled = true;
//step 3: Call the service to get contact info
return this.service.get(id)
})
.subscribe(contact => {
//Do the rest
this.contact = contact;
this.form.reset(this.contact);
this.formDisabled = false;
});
PS: I use object and array destructuring for more succinct and readable code.
If you want to call your this.service.get
parallel to the dropdown
service, put them in the same Observable.forkJoin
:
Observable.forkJoin([
this.countryService.getAll(),
this.categoryService.getAll(),
this.sectorService.getAll(),
this.route.paramMap.switchMap(({id}) => {
this.formDisabled = true;
return this.service.get(id);
})
])
.subscribe(([countries, categories, sectors, contact]) => {
//Assign the dropdown values
this.countries = countries;
this.categories = categories;
this.sectors = sectors;
//Do the rest
this.contact = contact;
this.form.reset(this.contact);
this.formDisabled = false;
})
If you want to listen to the changes to any of the observable grouped together, regardless who emits first, use combineLatest()
:
Observable.combineLatest(
Observable.forkJoin([
this.countryService.getAll(),
this.categoryService.getAll(),
this.sectorService.getAll()
]),
this.route.paramMap.switchMap(({id}) => {
this.formDisabled = true;
return this.service.get(id);
})
)
.subscribe(([countries, categories, sectors, contact]) => {
//Assign the dropdown values
this.countries = countries;
this.categories = categories;
this.sectors = sectors;
//Do the rest
this.contact = contact;
this.form.reset(this.contact);
this.formDisabled = false;
})
Upvotes: 6