So I have a form where a control is required only if another control has a value, for this I created the following structure
profileGroup = this.fb.group({
username: [''],
address: [
'',
[
(control: AbstractControl) => {
if (this.profileGroup?.get('username')?.value) {
return (Validators.required)(control);
}
return (Validators.nullValidator)(control);
},
],
],
});
this function works well, once the username
control gets a value, a Validators.required
is added to the address
control. However, despite the validator being added, the validity of the address
control doesn't update so the control is still marked as valid
.
If possible I would like to avoid using something like
this.profileGroup.get('username')?.valueChanges.subscribe(val=>{
this.profileGroup.get('address')?.updateValueAndValidity()
})
since there may be dynamic controls that I don't know the name of, and don't want to set an observable for each of them.
Here's a stack blitz of a working example https://stackblitz.com/edit/angular-10-formbuilder-example-m8qjq8?file=src/app/app.component.ts
CodePudding user response:
this function works well, once the username control gets a value, a Validators.required is added to the address control. However, despite the validator being added, the validity of the address control doesn't update so the control is still marked as valid.
Well, exactly that's not what happens. You have defined a custom
validator for address
FormControl, which gets executed when the address
FormControl value is updated. (PS: It's not a dynamic validator)
When you update the username
, the validators associated with it and its ancestors (profileGroup in your case) are called, the sibling FormControl validators are not triggered.
When you execute the below code, you are manually executing the validators associated with address
FormControl, and that doesn't update the FormControl validity. It simply executes the validators associated with the FormControl and return their result.
const validator = abstractControl.validator({} as AbstractControl);
In order to update the sibling i.e address
FormControl validity, you will have to call it's updateValueAndValidity()
.
Another alternative could be to define validators at FormGroup level, which will be executed automatically and you don't have to manually call updateValueAndValidity()
on any FormControl. But, with this approach the validation errors will be set at FormGroup level.
since there may be dynamic controls that I don't know the name of, and don't want to set an observable for each of them.
If you won't be aware of control names, how exactly you will be using them within custom validator as below, where username
FormControl is being referred?
this.profileGroup?.get('username')?.value
There is a way to call updateValueAndValidity()
on the formControl, without explicitly specifying it's name, BUT won't recommend it. You can loop over the profileGroup.controls
and execute updateValueAndValidity
on each control, followed by profileGroup itself. You would also have to take care of passing {onlySelf: true, emitEvent: false}
options to updateValueAndValidity
to avoid recursion.