I am using rxjs switchMap to get the latest form value from a subject, set a local variable and then perform a query based on that value.
Long story short I am populating select box options based on the current value of the form.
// Get current value of Record Form
this.recordFormService.recordForm$
.pipe(
switchMap(recordForm => {
// Get current Product Owner on Record Form
this.currentProductOwner = recordForm.value.ProductOwner;
// Search People
return this.recordsApiService.searchPeople$(this.currentProductOwner);
}),
catchError(err => {
console.error(`get recordForm$ failed ${err}`);
return throwError(err);
})
)
.subscribe(results => {
// Set Product Owners select box options
this.productOwners = results.data;
});
This is all working properly.
However, I also need to perform the same exact logic when a specific form value changes.
// Run the same logic when the ProductOwner value changes
this.parentFormGroup.controls['ProductOwner'].valueChanges.subscribe(val => {});
I tried putting the combineLatest operator at the top, however that only resolves when both observables have emitted at least one value.
The valueChanges
observable may not necessarily ever change, but recordFormService.recordForm$ will run at least once when the page is loaded.
Note: The result of the valueChanges
observable is a string and the result of the recordForm$
observable is a FormGroup object, they need to be processed entirely differently.
What operator can I use to achieve this requirement?
// Get latest value of all observables
combineLatest(
this.parentFormGroup.controls['ProductOwner'].valueChanges,
// Get current value of Record Form
this.recordFormService.recordForm$
)
.pipe(
switchMap(recordForm => {
// Get current Product Owner on Record Form
this.currentProductOwner = recordForm.value.ProductOwner;
// Search People
return this.recordsApiService.searchPeople$(this.currentProductOwner);
}),
catchError(err => {
console.error(`get recordForm$ failed ${err}`);
return throwError(err);
})
)
.subscribe(results => {
// Set Product Owners select box options
this.productOwners = results.data;
});
UPDATE:
I also tried merge
. However the results of both observables are entirely different (one is a formGroup and one is a string) and they both need to be processed differently.
// Get latest value of all observables
merge(
this.parentFormGroup.controls['ProductOwner'].valueChanges,
// Get current value of Record Form
this.recordFormService.recordForm$
)
You'll want merge :
import { merge } from 'rxjs';
Make 2 observables that emit the same type from the observables you want to react to. I am using a fake type RecordForm
which holds your form value and the product owner as an example:
const ownerChanges$: Observable<RecordForm> = this.parentFormGroup.controls['ProductOwner'].valueChanges.pipe(
withLatestFrom(this.recordFormService.recordForm$),
map(([owner, recordForm]) => ({ ...recordForm, owner }))
);
const formChanges$: Observable<RecordForm> = this.recordFormService.recordForm$.pipe(
withLatestFrom(this.parentFormGroup.controls['ProductOwner'].valueChanges),
map(([recordForm, owner]) => ({ ...recordForm, owner }))
);
Then pass them to merge:
// Get latest value of all observables
merge(ownerChanges$, recordChanges$).pipe(
...
// the type passed here is always RecordForm - react accordingly
)
.subscribe(results => {
// Set Product Owners select box options
this.productOwners = results.data;
});
But you'll want to make sure both observables passed to merge
are the same type, so they can be handled the same way.
I think what you want to do is create another downstream observable; the one for your form input that should trigger the API call as well as the original observable. To do this you can use mergeMap
pipe on the original Observable. This will create another Emitter inside that can emit in parallel from the first. Try this:
const formObs$ = this.parentFormGroup.controls['ProductOwner'].valueChanges;
this.recordFormService.recordForm$.pipe(
// give precedence in the merge callback to the original stream Observable,
// but if just the second Observable fires here, grab that value and use it to pass downstream
mergeMap(() => formObs$, (originalValue, formObsValue) => originalValue || formObsValue),
// now recordForm is either from the original observable, or from your form input
switchMap(recordForm => {
this.currentProductOwner = recordForm.value.ProductOwner;
// Search People
return this.recordsApiService.searchPeople$(this.currentProductOwner);
}),
catchError(err => {
console.error(`get recordForm$ failed ${err}`);
return throwError(err);
})
).subscribe(results => {
// Set Product Owners select box options
this.productOwners = results.data;
});
I'm making the assumption here that recordForm$ is returning a form value just like your second observable .valueChanges
. If that's not the case, you'll just need to massage it in the mergeMap 2nd callback function to produce a merged value to the switchMap that you want.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.