Perform Conditional Validation With the valueChanges Method in Angular Reactive Forms
In this post, we will learn to use Angular Reactive Form's value change detection and enable conditional validation on using this functionality.
Join the DZone community and get the full member experience.
Join For FreeLearn How to Create Your First Angular Reactive Form here
Let's say you have a Reactive Form created using the FormBuilder
class as shown below:
ngOnInit() {
this.loginForm = this.fb.group({
email: [null, Validators.required],
password: [null, [Validators.required, Validators.maxLength(8)]],
phonenumber: [null]
});
}
You have created loginForm
, which has three controls: email, password, and phone number. Here, you are using FormBuilder
to create a reactive form. On the component template, you can attach loginForm
as shown in the code below. Using property binding, the formGroup
property of the HTML form element is set to loginForm
and the formControlName
value of these controls are set to the individual FormControl
property of FormBuilder
.
<form (ngSubmit)='loginUser()' [formGroup]='loginForm' novalidate class="form">
<input formControlName='email' type="text" class="form-control" placeholder="Enter Email" />
<div class="alert alert-danger" *ngIf="loginForm.get('email').hasError('required') && loginForm.get('email').touched">
Email is required
</div>
<input formControlName='password' type="password" class="form-control" placeholder="Enter Password" />
<input formControlName='phonenumber' type="text" class="form-control" placeholder="Enter Phone Number" />
<div class="alert alert-danger" *ngIf="loginForm.get('phonenumber').hasError('required') && loginForm.get('phonenumber').touched">
Phone Number is required
</div>
<br />
<button [disabled]='loginForm.invalid' class="btn btn-default">Login</button>
</form>
We have also put error messages for email and phone number fields. Also, the submit button would only be enabled when the form is valid. Form submission is handled as shown in the listing below:
loginUser() {
console.log(this.loginForm.status);
console.log(this.loginForm.value);
}
If the form is valid in the browser console, you will get an output like below:
In addition, if there is an error, the submit button would be disabled and an error message will be shown as below:
Now assume a scenario that you have a radio button to send a notification. The user should able to select the send notification option, and, on basis of that, certain FormControl
s will have some validations.
Consider the form below:
We have added a Send Notification option. If the user selects Phone to send a notification, then the Phone Number field should be required, otherwise, it should not be. To achieve this, we need to perform following tasks:
- Listening to changes.
- Put conditional validation.
To start with, let us modify our form to handle the notification. So now the form has a notification FormControl
with the default value set to null as shown in the listing below :
this.loginForm = this.fb.group({
email: [null, Validators.required],
password: [null, [Validators.required, Validators.maxLength(8)]],
phonenumber: [null],
notification: ['email']
});
On the Reactive form template, we will add a radio button group to handle the Send Notification option.
<form (ngSubmit)='loginUser()' [formGroup]='loginForm' novalidate class="form">
<input formControlName='email' type="text" class="form-control" placeholder="Enter Email" />
<div class="alert alert-danger" *ngIf="loginForm.get('email').hasError('required') && loginForm.get('email').touched">
Email is required
</div>
<input formControlName='password' type="password" class="form-control" placeholder="Enter Password" />
<input formControlName='phonenumber' type="text" class="form-control" placeholder="Enter Phone Number" />
<div class="alert alert-danger" *ngIf="loginForm.get('phonenumber').hasError('required') && loginForm.get('phonenumber').touched">
Phone Number is required
</div>
<br />
<label class='control-label'>Send Notification</label>
<br />
<label class="radio-inline">
<input type="radio" value="email" formControlName="notification">Email
</label>
<label class="radio-inline">
<input type="radio" value="phone" formControlName="notification">Phone
</label>
<br />
<button [disabled]='loginForm.invalid' class="btn btn-default">Login</button>
</form>
At this point form, this looks like below:
Subscribe to valueChanges
In Reactive forms, both FormControls
and FormGroups
has a valueChanges method. It returns an observable type, so you can subscribe to it, to work with real-time value changing of FormControls
or FormGroups
.
In our example, we need to subscribe to the valueChanges
method of the notification for FormControl
. This can be done as below:
formControlValueChanged() {
this.loginForm.get('notification').valueChanges.subscribe(
(mode: string) => {
console.log(mode);
});
}
We need to call this using an OnInit
lifecycle hook a shown below:
ngOnInit() {
this.loginForm = this.fb.group({
email: [null, Validators.required],
password: [null, [Validators.required, Validators.maxLength(8)]],
phonenumber: [null],
notification: ['email']
});
this.formControlValueChanged();
}
Now when you change the selection for the notification on the form in the browser console you can see that you have the most recent value.
Keep in mind that we are not handling any event on the radio button to get the latest value. Angular has a valueChanges
method which returns recent values as observables on the FormControl
and FormGroup
, and we are subscribed to that for the recent value on the notification FormControl
.
Conditional Validation
Our requirement is that when notification
is set to phone
then the phonenumber
FormControl should be a required field and if it is set to email
then phonenumber
FormControl should not have any validation.
Let us modify the formControlValueChnaged()
function as shown in the next listing to enable conditional validation on the phonenumber
FormControl.
formControlValueChanged() {
const phoneControl = this.loginForm.get('phonenumber');
this.loginForm.get('notification').valueChanges.subscribe(
(mode: string) => {
console.log(mode);
if (mode === 'phone') {
phoneControl.setValidators([Validators.required]);
}
else if (mode === 'email') {
phoneControl.clearValidators();
}
phoneControl.updateValueAndValidity();
});
}
There is a lot of code above, so let's walk through it line by line.
- Using the
get
method of FormBuilder, we get an instance of the phone number FormControl. - We then subscribe to the
valueChanges
method of the notification FormControl. - Check the current value of the notification FormControl
- If the current value is
phone
, using thesetValidators
method of FormControl to set the required validator on thephonenumber
control. - If the current value is
email
, using theclearValidators
method of FormControl we clear all validations on thephonenumber
control. - Lastly, we call the
updateValueAndValidity
method to update the validation rules ofphonecontrol
.
Run the application and you will see that as you change notification values, the validation of phonenumber
is getting changed:
By using the power of Angular Reactive Form's valueChanges
method, you can achieve conditional validations and many other functionalities, such as reacting to changes in the underlying data model of the reactive form.
Published at DZone with permission of Dhananjay Kumar, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments