I am trying to access the valueChanges of another field (B) in an asynchronous validator (of field A). But the validation is only triggered when the value of field A changes.
If only the value of B changes, the status remains "PENDING" until the value of A is changed.
What I want to do is create a validator that triggers when the given observables emits a value AND when the value of field B changes. I can imagine that the problem has something to do with the valueChanges observable not emitting any value, but how do I fix that?
StackBlitz which recreates the problem: https://stackblitz.com/edit/angular-ivy-epoeen?devtoolsheight=33&file=src/app/app.component.ts
Example component:
export class AppComponent {
public form = new FormGroup({
fieldA: new FormControl(null, { updateOn: 'blur' }),
fieldB: new FormControl(null, { updateOn: 'blur' }),
});
constructor() {
this.fieldBControl.setAsyncValidators(
FieldBValidators.asyncValidateFieldBShouldNotBe(this.fieldA$)
);
this.fieldA$.subscribe((value) => console.log('fieldA changed to', value));
this.fieldB$.subscribe((value) => console.log('fieldB changed to', value));
this.fieldBStatus$.subscribe((status) => console.log('status changed to', status));
}
public get fieldAControl(): FormControl {
return this.form.get('fieldA') as FormControl;
}
public get fieldA$(): Observable<string> {
return this.fieldAControl.valueChanges;
}
public get fieldBControl(): FormControl {
return this.form.get('fieldB') as FormControl;
}
public get fieldB$(): Observable<string> {
return this.fieldBControl.valueChanges;
}
public get fieldBStatus$(): Observable<string> {
return this.fieldBControl.statusChanges;
}
}
Validator:
export class FieldBValidators {
public static asyncValidateFieldBShouldNotBe(
value$: Observable<string>
): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors> =>
value$.pipe(
tap((value) => {
console.log('fieldA', value);
console.log('fieldB', control.value);
}),
take(1),
map(
(value): ValidationErrors => {
if (value === control.value) {
return { error: true };
}
return null;
}
)
);
}
}
Console output is like this:
app.component.ts:24 fieldA changed to a
fieldB.validators.ts:23 fieldA a
fieldB.validators.ts:24 fieldB null
app.component.ts:25 fieldB changed to a
app.component.ts:27 status changed to PENDING
app.component.ts:25 fieldB changed to b
app.component.ts:27 status changed to PENDING
app.component.ts:24 fieldA changed to b
fieldB.validators.ts:23 fieldA b
fieldB.validators.ts:24 fieldB b
app.component.ts:27 status changed to INVALID
My attempt was to add a startWith pipe to the value, but then the Validator function always gets the initial value.
Thank you very much for any input. Have a nice day!