With my current code I am able to make inputs show up as invalid if they are blank or do not fit the format of the input's type (eg: email must have the '@' and '.' format).
My next step is to make sure the password and confirmPassword fields match up. I have created a function that compares the two, but am having a lot of trouble implementing it with the mdbootstrap form.
The function that I have been playing around with:
mustMatch(controlName: string, matchingControlName: string) {
return (formGroup: FormGroup) => {
const control = formGroup.controls[controlName];
const matchingControl = formGroup.controls[matchingControlName];
if (matchingControl.errors && !matchingControl.errors.mustMatch) {
// var passwordInput = document.getElementById('password');
// passwordInput.classList.remove('ng-valid');
// passwordInput.classList.add('ng-invalid');
formGroup.controls['password'].setErrors({'incorrect': true});
return;
}
if (control.value !== matchingControl.value) {
matchingControl.setErrors({ mustMatch: true });
} else {
matchingControl.setErrors(null);
}
};
}
The elements I need to make invalid:
<div class="col-xs-12">
<div class="form-outline">
<input formControlName="password" type="password" id="password" class="form-control pb-3" required/>
<label class="form-label" for="password">Password</label>
<div class="invalid-feedback">Please enter your password.</div>
</div>
</div>
<div class="col-xs-12">
<div class="form-outline">
<input formControlName="confirmPassword" type="password" id="confirmPass" class="form-control pb-3" required/>
<label class="form-label" for="confirmPass">Confirm Password</label>
<div class="invalid-feedback">Please confirm your password.</div>
</div>
</div>
The initialization of the form:
ngOnInit(): void {
document.querySelectorAll('.form-outline').forEach((formOutline) => {
new mdb.Input(formOutline).init();
});
this.setupSignupForm();
const forms = document.querySelectorAll('.needs-validation');
Array.prototype.slice.call(forms).forEach((form) => {
form.addEventListener('submit', (event) => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
setupSignupForm(): void {
this.signupForm = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['',
[
Validators.required,
Validators.email,
]],
confirmEmail: ['',
[
Validators.required,
Validators.email,
]],
joinCode: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['',
[
Validators.required,
]]
}, {
validators: [this.mustMatch('password', 'confirmPassword'), this.mustMatch('email', 'confirmEmail')]
});
}
Please let me know if you can figure out how to do this. I have been bashing my head against the wall for a while on this problem!
I think it's better to use the validator
for each form-control
you need instead of the whole form-group
, because:
form-group
validators will be checked on any changes within the form, even if the changes are not related to the target form-control
(s) (password
and email
in your case).setErrors
) manually, and the same form removing them.Instead of that you can achieve it by assigning the validator to the form-control
itself, like the following:
setupSignupForm(): void {
this.signupForm = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
confirmEmail: [
'',
[Validators.required, Validators.email, this.mustMatch('email')]
],
joinCode: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['', [Validators.required, this.mustMatch('password')]]
});
}
mustMatch(matchingControlName: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.parent) return;
const matchingCtrlValue = control.parent.get(matchingControlName).value;
if (control.value !== matchingCtrlValue) {
return { mustMatch: true };
} else {
return null;
}
};
}