I have an use case in which I need to verify a few conditions. If the previous one fails I should not verify next one but based on the previous one I should display dialog with title and description indicates what went wrong. Assuming I have an service (pseudo code) // verify-condition.service.ts
public validateConditions():Observable<ValidationData> {
public validateConditions(): Observable<ValidationModal> {
const hasContract$: Observable<ContractStatus> = this.getContractSignStatus();
const hasTUTSigned$: Observable<boolean> = this.hasTUTSigned();
const doData$: Observable<DoModel> = this.getDoData();
return combineLatest([hasContract$, hasTUTSigned$, doData$]).pipe(
map(([hasContract, hasTUTSigned, doData,]) => {
const validationConditions: ValidationModal = {
conditions: {
hasContract: hasContract === ContractStatus.SIGNED,
hasTUTSigned,
wasSigned: doData.wasSigned,
valid: doData.valid,
signDate: doData.signDate,
}
};
return validationConditions;
})
);
}
}
and then in the component I'm using it
public verifyProcess(): void {
verifyConditionService.validateConditions().pipe(take(1)).subscribe((validationData:ValidationData=> {
if (validationData) {
this.modalValidationService.openModal(validationData);
} else {
this.beginProcess();
}
})
}
Thing is that in the service I run all the conditions at once using combineLatest but I would like to run them one by one and if the first fails I should not run the next one but instead throw an error or in some other way indicate that it fails and return data needed to display dialog.
public validateConditionsInOrder():Observable<ValidationData|boolean> {
return this.getContractSignStatus().pipe(
map((signStatus:ContractStatus)=>{
if(signStatus !== ContractStatus.SIGNED){
return {
hasContract:signStatus
}
} else {
return true;
}
}),
switchMap((previousCondition)=>{
if(!previousCondition){ // so if previous condition failed I DO NOT want to check the next conditions but instead stop verification and return the data from the previous condition
} else {
this.hasTUTSigned(); // if the previous condition is OK, I move on to the next one and so on. and if here or the next condition fails I always need to know what went wrong in orther to display dialog based on the info
}
})
)
}
You can probably create a chain of observables/calls using iif
operator. Not the nicest solution, but you can use it as an inspiration and implement a custom pipe, as @Will Alexander has mentioned.
public validateConditions(): Observable<ValidationModal> {
// Prepared validateConditions object, so that you partial Observables
// have something to work with. Adjust accordingly
const result = {
hasContract: false,
hasTUTSigned: false,
wasSigned: false,
valid: null,
signDate: null,
};
// Map hasContract state directly and use it later
const hasContract$: Observable<ContractStatus> =
this.getContractSignStatus().pipe(
map((hasContract) => ({
...result,
hasContract: hasContract === ContractStatus.SIGNED,
}))
);
const hasTUTSigned$: Observable<boolean> = this.hasTUTSigned();
const doData$: Observable<DoModel> = this.getDoData();
// Instead of combineLatest, start with piping hasContract$
return hasContract$.pipe(
switchMap((validateConditions) =>
iif(
//If hasContract is true, chain hasTUTSigned$ and map result
//Otherwise return previous state of validateConditions
() => validateConditions.hasContract,
hasTUTSigned$.pipe(
map((hasTUTSigned) => ({ ...validateConditions, hasTUTSigned }))
),
of(validateConditions) // Return unchanged data
)
),
switchMap((validateConditions) =>
iif(
// Same as before, but check different condition, map different data
() => validateConditions.hasTUTSigned,
doData$.pipe(map((data) => ({ ...validateConditions, ...data }))),
of(validateConditions) // Return unchanged data
)
)
);
}
Using iif
and of
you can either chain real observables or pass unmodified validateConditions object if condition weren't met.