I'm trying to write an obervable for validation task. I have 2 observables that I want to process in order, frontEndValidate and backEndValidate. If one throws an error, the pipeline should stop. These 2 observables will be emitted at runtime when I click validate button, by calling next() inside validate().
In the pipeline i check if the emitted obervables is valid, if so continue, else throw an error.
This pipeline works fine if those 2 observable emit of({ valid: true}). But if it emits of({valid:false}), my validationAction$ wil be dead and the validate button will not work.
I also cannot use catchError to keep the pipeline alive, sine the validation will continue no matter what state of the observable is.
For short, I want my pipeline to behave like this and still alive.
Thank you.
private validationSubject = new Subject<Observable<any>>();
validationAction$ = this.validationSubject.asObservable().pipe(
concatMap((item) =>
from(item)
.pipe(
map((result) => {
console.log("result", result);
if (!result.valid) throw new Error();
return result
}),
)
),
);
validate() {
this.validationSubject.next(this.frontEndValidate());
this.validationSubject.next(this.backEndValidate());
}
/* Front-end validation */
/** */
frontEndValidate(): Observable < any > {
// Validation goes here
return of({ valid: true, name: 'frontend', msg: 'Passed' }).pipe(
tap(() => {
console.log('frontend validation...starts');
}),
delay(3000), // mimic delay
tap(() => {
console.log('frontend validation...finished')
}),
);
}
/* Back-end validation */
/** */
backEndValidate(): Observable < any > {
// Validation goes here
return of({ valid: true, name: 'backend', msg: 'Passed' }).pipe(
tap(() => {
console.log('backend validation...starts');
}),
delay(3000), // mimic delay
tap(() => {
console.log('backend validation...finished')
}),
);
}
How about something like this:
validate() {
const result$ = combineLatest([
this.frontEndValidate(),
this.backEndValidate(),
])
.pipe(
tap(([frontEndResult, backEndResult]) => {
// Do whatever else here
})
);
result$.subscribe(x => console.log('Result: ', JSON.stringify(x)));
}
Then you don't need the subject. Just use combineLatest to combine the two results and ensure the code that follows it waits for both to complete before continuing.
The result emits an array with the two objects defined in your validation methods. To instead emit only an array with the Boolean values, try this:
validate() {
const result$ = combineLatest([
this.frontEndValidate(),
this.backEndValidate(),
]).pipe(
map(([frontEndResult, backEndResult]) =>
([frontEndResult.valid, backEndResult.valid])
)
);
result$.subscribe((x) => console.log('Result: ', JSON.stringify(x)));
}
Working example: https://stackblitz.com/edit/angular-rxjs-validation-deborahk