See updates below
I realize my problem is that I'm very green at observables and RxJS.
I have a custom validator like this:
export function ValidateFinalQty(service: MyService) {
return (control: AbstractControl): { [key: string]: any } | null => {
let qty = service.GetQty();
if (control.value != qty) {
return { FinalQuantityNotMatching: true };
} else {
return null;
}
};
}
GetQty returns an RxJS Observable.
So how can I set this up so that my synchronous validator returns the right value depending on an asynchronous call? I need the return type of the validator to stay as { [key: string]: any } | null.
I saw a suggestion of something like qty = await service.GetQty().first().toPromise(); but then I am returning a promise and I can't return a promise for the validator to work as I understand.
How can I handle this?
From my package.json:
"@angular/core": "7.1.0",
"@angular/forms": "7.1.0",
"rxjs": "6.3.3",
"rxjs-compat": "^6.4.0",
UPDATE 5/23/19 Trying to Implement @Sachin's Answer. My breakpoints inside of map never get hit. I don't get any console logs and even if I remove the logic in the map and return null, it still always returns invalid. Very confused about what's happening here. My service is actually getting called, I've confirmed that.
Any thoughts?
export class CustomAsyncValidator {
static ValidateFinalQty(qtyService: FinalQtyService, brf: BatchRecordForm): AsyncValidatorFn {
return (control: AbstractControl) => {
return qtyService.postCalcFinalQuanity(brf)
.pipe(
map((qty) => {
console.log("running qty validator. value:", qty);
if (control.value !== qty) {
return { FinalQuantityNotMatching: true };
} else {
return null;
}
}),
catchError((err) => {
console.log("Error in final quantity validator", err);
return null;
}),
finalize(() => console.log("finished"))
);
};
}
}
UPDATE 6/7/2019
At the subscribe logging I am getting the right answer (null or { FinalQuantityNotMatching: true }) but my form control remains invalid. What am I doing wrong?
Validator.ts
export class CustomAsyncValidator {
static ValidateFinalQty(fqs: FinalQtyService, brf: BatchRecordForm) {
return (control: AbstractControl) => {
return fqs.postCalcFinalQuanity(brf).pipe(
debounceTime(500),
tap((action) => console.log("final qty", action)),
tap((action) => console.log("control.value", control.value)),
map(arr => (arr.Value !== `${control.value}`) ? { FinalQuantityNotMatching: true } : null)
).subscribe(x => console.log("subscribe output", x));
};
}
}
component.ts
this.noteForm.addControl(this.finalQtyFormControlName, new FormControl(this.noteSubModuleForm.Value,
[Validators.required, CustomAsyncValidator.ValidateFinalQty(this.finalQtyService, this.embrService.batchRecordForm)]));
UPDATE 6/7/2019 #2
Following https://www.youtube.com/watch?v=zeX5CtFqkXQ I was able to make a directive based validator but would still prefer the have the validator in my ts if you're able to see what I did wrong in the previous update.
@Directive({ selector: "[validFinalQty]", providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: ValidateFinalQtyDirective, multi: true }] })
export class ValidateFinalQtyDirective implements AsyncValidator {
constructor(private fqs: FinalQtyService, private embrService: EmbrService) { }
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.fqs.postCalcFinalQuanity(this.embrService.batchRecordForm).pipe(
tap(x => {
console.log("final qty", x);
console.log("control.value", control.value);
}),
map(arr => (arr.Value !== `${control.value}`) ? { FinalQuantityNotMatching: true } : null)
);
}
getQty()?.subscribe(x => console.log("subscribe output", x));this subscription is wrong. You should pass the observable without subscribing