I'm trying to build out a registration form in Angular 2 using the Reactive Forms module. As such, I have a FormGroup defined for the form, and I can then list validators for each FormControl therein.
Consider this partial class:
export class TestFormComponent implements OnInit {
form: FormGroup;
password = new FormControl("", [Validators.required]);
passwordConfirm = new FormControl("", [Validators.required, this.validatePasswordConfirmation]);
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.form = this.fb.group({
"password": this.password,
"passwordConfirm": this.passwordConfirm
});
}
validatePasswordConfirmation(fc: FormControl) {
var pw2 = fc.value;
var pw = // how do I get this value properly????
if (pw === '') {
return {err:"Password is blank"};
}
if (pw2 === '') {
return {err:"Confirmation password is blank"};
}
if (pw !== pw2) {
return {err:"Passwords do not match"}
}
return null;
}
}
passwordConfirm
password
pw
this.form.value.password
this
So the answer turns out to be putting a new validator on the form as a whole, and then using the FormGroup object that is passed to the validator as a way to compare the field values. That much I had suspected. What I was missing, however, was how to set the error state properly on the individual passwordConfirm
field. This code shows how to do it:
export class TestFormComponent implements OnInit {
form: FormGroup;
password = new FormControl("", [Validators.required]);
passwordConfirm = new FormControl("", [Validators.required, this.validatePasswordConfirmation]);
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.form = this.fb.group({
"password": this.password,
"passwordConfirm": this.passwordConfirm
},
{
validator: this.validatePasswordConfirmation
});
}
validatePasswordConfirmation(group: FormGroup) {
var pw = group.controls['password'];
var pw2 = group.controls['passwordConfirm'];
if (pw.value !== pw2.value) { // this is the trick
pw2.setErrors({validatePasswordConfirmation: true});
}
// even though there was an error, we still return null
// since the new error state was set on the individual field
return null;
}
}
The trick, as mentioned in the comment in the code above, is that you can set error states on individual FormControl
fields with the setErrors()
method. So now, with this code in place, the confirmation field gets the proper valid/invalid state set based upon the regular validators it has, like Validators.required
, as well as from the custom form based validator we added.
With this method, you could create complex form-based validators that can check the states of many different form fields and set validation states on each individually based on any business logic you can come up with. This makes cross-field validation with Angular 2 Reactive forms quite simple.