Issue
I am trying to implement confirm password validator in the register form but doesn't work. I am using Angular 15 in my app.
My code:
import { Component } from '@angular/core';
import { AbstractControl, AbstractControlOptions, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AccountService } from '../account.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent {
constructor(private fb: FormBuilder, private accountService: AccountService, private router: Router) {}
complexPassword = "(?=^.{8,20}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{":;'?/>.<,])(?!.*\s).*$";
registerForm = this.fb.group({
displayName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(50)]],
email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(50), Validators.email]],
gender: [''],
firstName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(50)]],
lastName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(50)]],
dateOfBirth: [null],
street: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(50)]],
postalCode: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(20)]],
city: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(50)]],
password: ['', [Validators.required, Validators.pattern(this.complexPassword)]],
confirmPassword: ['', Validators.required]
},
{ validators: this.passwordMatchValidator} as AbstractControlOptions
);
// passwordMatchValidator(g: FormGroup) {
passwordMatchValidator(group: AbstractControl) {
return group.get('password').value === group.get('confirmPassword').value
? null
: { mismatch: true };
}
onSubmit() {
this.accountService.register(this.registerForm.value).subscribe({
next: () => this.router.navigateByUrl('/shop')
})
}
<div class="d-flex justify-content-center mt-5">
<div class="col-3">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div class="text-center mb-4">
<h1 class="mb-3">Sign up</h1>
</div>
<app-text-input [formControl]="registerForm.controls['displayName']" [label]="'Display Name'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['email']" [label]="'Email'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['password']" [label]="'Password'" [type]="'password'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['confirmPassword']" [label]="'Confirm Password'" [type]="'password'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['gender']" [label]="'Gender'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['firstName']" [label]="'First Name'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['lastName']" [label]="'Last Name'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['dateOfBirth']" [label]="'Date of Birth'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['street']" [label]="'Street'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['postalCode']" [label]="'Postal Code'"></app-text-input>
<app-text-input [formControl]="registerForm.controls['city']" [label]="'City'"></app-text-input>
<div class="d-grid">
<button [disabled]="registerForm.invalid" class="btn btn-lg btn-primary mt-3" type="submit">Sign up</button>
</div>
</form>
</div>
</div>
I am using a reusable text input component:
import { Component, Input, Self } from '@angular/core';
import { ControlValueAccessor, FormControl, NgControl } from '@angular/forms';
@Component({
selector: 'app-text-input',
templateUrl: './text-input.component.html',
styleUrls: ['./text-input.component.scss']
})
export class TextInputComponent implements ControlValueAccessor {
@Input() type = 'text';
@Input() label = '';
constructor(@Self() public controlDir: NgControl) {
this.controlDir.valueAccessor = this;
}
writeValue(obj: any): void {
}
registerOnChange(fn: any): void {
}
registerOnTouched(fn: any): void {
}
get control(): FormControl {
return this.controlDir.control as FormControl;
}
}
<div class="form-floating mb-3">
<input
type={{type}}
[formControl]="control"
placeholder="{{label}}"
class="form-control"
[ngClass]="(control.touched) ? control.invalid ? 'is-invalid' : 'is-valid' : null"
>
<label for="floatingInput">{{label}}</label>
<div *ngIf="control.errors?.['required']" class="invalid-feedback">Please enter your {{label}}</div>
<div *ngIf="control.errors?.['email']" class="invalid-feedback">Invalid email address</div>
<div *ngIf="control.errors?.['pattern']" class="invalid-feedback">Password not complex enough</div>
<div *ngIf="control.errors?.['confirmPassword']" class="invalid-feedback">Passwords must match</div>
</div>
As you can see on the screenshot I got no error message about password mismatching.
Please understand I'm not a professional developer. Thanks in advance for any advice!
Solution
When you use validators over the whole form who is invalid is the form -not the formControl-
So your ngClass should be like:
[ngClass]="(control.touched) ? control.invalid
|| registerForm.errors?.mismatch ? 'is-invalid' : 'is-valid' : null"
NOTE: It's unnecessary create a custom formControl (a component that implements ControlValueAccesor) if only want to add a label and a type. You can use viewProviders, see, e.g. this SO
Answered By - Eliseo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.