I want to perform password and confirm password validations using material components only,and an error message below the confirm password field if confirm password field doesn't match
And if it is empty
.Tried many resources unable to achieve.
Tried this video too.
This is the material component i am looking for
HTML
<mat-form-field >
<input matInput placeholder="New password" [type]="hide ? 'password'
: 'text'" [formControl]="passFormControl" required>
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' :
'visibility_off'}}</mat-icon>
<mat-error *ngIf="passFormControl.hasError('required')">
Please enter your newpassword
</mat-error>
</mat-form-field>
<mat-form-field >
<input matInput placeholder="Confirm password" [type]="hide ?
'password' : 'text'" [formControl]="confirmFormControl"
required>
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' :
'visibility_off'}}</mat-icon>
<mat-error *ngIf="confirmFormControl.hasError('required')">
Confirm your password
</mat-error>
</mat-form-field>
TS
import {Component, OnInit } from '@angular/core';
import {FormControl, FormGroupDirective, NgForm, Validators} from
'@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
@Component({
selector: 'asd-set-pass',
templateUrl: './set-pass.component.html',
styleUrls: ['./set-pass.component.css']
})
passFormControl = new FormControl('', [
Validators.required,
]);
confirmFormControl = new FormControl('', [
Validators.required,
]);
hide =true;
}
It's validating the following conditions fine 1)If password and confirm password fields are empty its showing error text.
I want to compare to fields in (.ts) file like how its validating for empty field, and an error to come if confirm password field is empty.
This question is related to
angular
angular-material
angular-forms
You can use this way to fulfill this requirement. I use the below method to validate the Password and Confirm Password.
To use this method you have to import FormGroup
from @angular/forms
library.
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
FormBuilder Group:
this.myForm= this.formBuilder.group({
password : ['', Validators.compose([Validators.required])],
confirmPassword : ['', Validators.compose([Validators.required])],
},
{validator: this.checkPassword('password', 'confirmPassword') }
);
Method to Validate two fields:
checkPassword(controlName: string, matchingControlName: string) {
return (formGroup: FormGroup) => {
const control = formGroup.controls[controlName];
const matchingControl = formGroup.controls[matchingControlName];
if (matchingControl.errors && !matchingControl.errors.mustMatch) {
// return if another validator has already found an error on the matchingControl
return;
}
// set error on matchingControl if validation fails
if (control.value !== matchingControl.value) {
matchingControl.setErrors({ mustMatch: true });
this.isPasswordSame = (matchingControl.status == 'VALID') ? true : false;
} else {
matchingControl.setErrors(null);
this.isPasswordSame = (matchingControl.status == 'VALID') ? true : false;
}
}
}
HTML: Here I am use personalized isPasswordSame variable you can use the inbuilt hasError or any other.
<form [formGroup]="myForm">
<ion-item>
<ion-label position="floating">Password</ion-label>
<ion-input required type="text" formControlName="password" placeholder="Enter Password"></ion-input>
</ion-item>
<ion-label *ngIf="myForm.controls.password.valid">
<p class="error">Please enter password!!</p>
</ion-label>
<ion-item>
<ion-label position="floating">Confirm Password</ion-label>
<ion-input required type="text" formControlName="confirmPassword" placeholder="Enter Confirm Password"></ion-input>
</ion-item>
<ion-label *ngIf="isPasswordSame">
<p class="error">Password and Confrim Password must be same!!</p>
</ion-label>
</form>
Single method for Reactive Forms
TYPESCRIPT
// All is this method
onPasswordChange() {
if (this.confirm_password.value == this.password.value) {
this.confirm_password.setErrors(null);
} else {
this.confirm_password.setErrors({ mismatch: true });
}
}
// getting the form control elements
get password(): AbstractControl {
return this.form.controls['password'];
}
get confirm_password(): AbstractControl {
return this.form.controls['confirm_password'];
}
HTML
// PASSWORD FIELD
<input type="password" formControlName="password" />
// CONFIRM PASSWORD FIELD
<input type="password" formControlName="confirm_password" (change)="onPasswordChange()" />
// SHOW ERROR IF MISMATCH
<span *ngIf="confirm_password.hasError('mismatch')">Password do not match.</span>
It's not necessary to use nested form groups and a custom ErrorStateMatcher for confirm password validation. These steps were added to facilitate coordination between the password fields, but you can do that without all the overhead.
Here is an example:
this.registrationForm = this.fb.group({
username: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
password1: ['', [Validators.required, (control) => this.validatePasswords(control, 'password1') ] ],
password2: ['', [Validators.required, (control) => this.validatePasswords(control, 'password2') ] ]
});
Note that we are passing additional context to the validatePasswords method (whether the source is password1 or password2).
validatePasswords(control: AbstractControl, name: string) {
if (this.registrationForm === undefined || this.password1.value === '' || this.password2.value === '') {
return null;
} else if (this.password1.value === this.password2.value) {
if (name === 'password1' && this.password2.hasError('passwordMismatch')) {
this.password1.setErrors(null);
this.password2.updateValueAndValidity();
} else if (name === 'password2' && this.password1.hasError('passwordMismatch')) {
this.password2.setErrors(null);
this.password1.updateValueAndValidity();
}
return null;
} else {
return {'passwordMismatch': { value: 'The provided passwords do not match'}};
}
Note here that when the passwords match, we coordinate with the other password field to have its validation updated. This will clear any stale password mismatch errors.
And for completeness sake, here are the getters that define this.password1
and this.password2
.
get password1(): AbstractControl {
return this.registrationForm.get('password1');
}
get password2(): AbstractControl {
return this.registrationForm.get('password2');
}
I found a bug in AJT_82's answer. Since I do not have enough reputation to comment under AJT_82's answer, I have to post the bug and solution in this answer.
Here is the bug:
Solution: In the following code:
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const invalidCtrl = !!(control && control.invalid && control.parent.dirty);
const invalidParent = !!(control && control.parent && control.parent.invalid && control.parent.dirty);
return (invalidCtrl || invalidParent);
}
}
Change control.parent.invalid
to control.parent.hasError('notSame')
will solve this problem.
After the small changes, the problem solved.
Edit: To validate the Confirm Password field only after the user starts typing you can return this instead
return ((invalidCtrl || invalidParent) && control.valid);
My answer is very simple>i have created password and confirm password validation using template driiven in angular 6
My html file
<div class="form-group">
<label class="label-sm">Confirm Password</label>
<input class="form-control" placeholder="Enter Password" type="password" #confirm_password="ngModel" [(ngModel)]="userModel.confirm_password" name="confirm_password" required (keyup)="checkPassword($event)" />
<div *ngIf="confirm_password.errors && (confirm_password.dirty||confirm_password.touched||signup.submitted)">
<div class="error" *ngIf="confirm_password.errors.required">Please confirm your password</div>
</div>
<div *ngIf="i" class='error'>Password does not match</div>
</div>
My typescript file
public i: boolean;
checkPassword(event) {
const password = this.userModel.password;
const confirm_new_password = event.target.value;
if (password !== undefined) {
if (confirm_new_password !== password) {
this.i = true;
} else {
this.i = false;
}
}
}
when clicking on submit button i check whether value of i is true or false
if true
if (this.i) {
return false;
}
else{
**form submitted code comes here**
}
The simplest way imo:
(It can also be used with emails for example)
public static matchValues(
matchTo: string // name of the control to match to
): (AbstractControl) => ValidationErrors | null {
return (control: AbstractControl): ValidationErrors | null => {
return !!control.parent &&
!!control.parent.value &&
control.value === control.parent.controls[matchTo].value
? null
: { isMatching: false };
};
}
In your Component:
this.SignUpForm = this.formBuilder.group({
password: [undefined, [Validators.required]],
passwordConfirm: [undefined,
[
Validators.required,
matchValues('password'),
],
],
});
Follow up:
As others pointed out in the comments, if you fix the error by fixing the password
field the error won't go away, Because the validation triggers on passwordConfirm
input. This can be fixed by a number of ways. I think the best is:
this.SignUpForm .controls.password.valueChanges.subscribe(() => {
this.SignUpForm .controls.confirmPassword.updateValueAndValidity();
});
On password
change, revliadte confirmPassword
.
I am using angular 6 and I have been searching on best way to match password and confirm password. This can also be used to match any two inputs in a form. I used Angular Directives. I have been wanting to use them
ng g d compare-validators --spec false and i will be added in your module. Below is the directive
import { Directive, Input } from '@angular/core';
import { Validator, NG_VALIDATORS, AbstractControl, ValidationErrors } from '@angular/forms';
import { Subscription } from 'rxjs';
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[compare]',
providers: [{ provide: NG_VALIDATORS, useExisting: CompareValidatorDirective, multi: true}]
})
export class CompareValidatorDirective implements Validator {
// tslint:disable-next-line:no-input-rename
@Input('compare') controlNameToCompare;
validate(c: AbstractControl): ValidationErrors | null {
if (c.value.length < 6 || c.value === null) {
return null;
}
const controlToCompare = c.root.get(this.controlNameToCompare);
if (controlToCompare) {
const subscription: Subscription = controlToCompare.valueChanges.subscribe(() => {
c.updateValueAndValidity();
subscription.unsubscribe();
});
}
return controlToCompare && controlToCompare.value !== c.value ? {'compare': true } : null;
}
}
Now in your component
<div class="col-md-6">
<div class="form-group">
<label class="bmd-label-floating">Password</label>
<input type="password" class="form-control" formControlName="usrpass" [ngClass]="{ 'is-invalid': submitAttempt && f.usrpass.errors }">
<div *ngIf="submitAttempt && signupForm.controls['usrpass'].errors" class="invalid-feedback">
<div *ngIf="signupForm.controls['usrpass'].errors.required">Your password is required</div>
<div *ngIf="signupForm.controls['usrpass'].errors.minlength">Password must be at least 6 characters</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="bmd-label-floating">Confirm Password</label>
<input type="password" class="form-control" formControlName="confirmpass" compare = "usrpass"
[ngClass]="{ 'is-invalid': submitAttempt && f.confirmpass.errors }">
<div *ngIf="submitAttempt && signupForm.controls['confirmpass'].errors" class="invalid-feedback">
<div *ngIf="signupForm.controls['confirmpass'].errors.required">Your confirm password is required</div>
<div *ngIf="signupForm.controls['confirmpass'].errors.minlength">Password must be at least 6 characters</div>
<div *ngIf="signupForm.controls['confirmpass'].errors['compare']">Confirm password and Password dont match</div>
</div>
</div>
</div>
I hope this one helps
Just do a standard custom validator and verify first if the form itself is defined, otherwise it will throw an error that says the form is undefined, because at first it will try to run the validator before the form is constructed.
// form builder
private buildForm(): void {
this.changePasswordForm = this.fb.group({
currentPass: ['', Validators.required],
newPass: ['', Validators.required],
confirmPass: ['', [Validators.required, this.passwordMatcher.bind(this)]],
});
}
// confirm new password validator
private passwordMatcher(control: FormControl): { [s: string]: boolean } {
if (
this.changePasswordForm &&
(control.value !== this.changePasswordForm.controls.newPass.value)
) {
return { passwordNotMatch: true };
}
return null;
}
It just checks that the new password field has the same value that the confirm password field. Is a validator specific for the confirm password field instead of the whole form.
You just have to verify that this.changePasswordForm
is defined because otherwise it will throw an undefined error when the form is built.
It works just fine, without creating directives or error state matchers.
*This solution is for reactive-form
You may have heard the confirm password is known as cross-field validation. While the field level validator that we usually write can only be applied to a single field. For cross-filed validation, you probably have to write some parent level validator. For specifically the case of confirming password, I would rather do:
this.form.valueChanges.subscribe(field => {
if (field.password !== field.confirm) {
this.confirm.setErrors({ mismatch: true });
} else {
this.confirm.setErrors(null);
}
});
And here is the template:
<mat-form-field>
<input matInput type="password" placeholder="Password" formControlName="password">
<mat-error *ngIf="password.hasError('required')">Required</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" placeholder="Confirm New Password" formControlName="confirm">`enter code here`
<mat-error *ngIf="confirm.hasError('mismatch')">Password does not match the confirm password</mat-error>
</mat-form-field>
You can simply use password field value as a pattern for confirm password field. For Example :
<div class="form-group">
<input type="password" [(ngModel)]="userdata.password" name="password" placeholder="Password" class="form-control" required #password="ngModel" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
<div *ngIf="password.invalid && (myform.submitted || password.touched)" class="alert alert-danger">
<div *ngIf="password.errors.required"> Password is required. </div>
<div *ngIf="password.errors.pattern"> Must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters.</div>
</div>
</div>
<div class="form-group">
<input type="password" [(ngModel)]="userdata.confirmpassword" name="confirmpassword" placeholder="Confirm Password" class="form-control" required #confirmpassword="ngModel" pattern="{{ password.value }}" />
<div *ngIf=" confirmpassword.invalid && (myform.submitted || confirmpassword.touched)" class="alert alert-danger">
<div *ngIf="confirmpassword.errors.required"> Confirm password is required. </div>
<div *ngIf="confirmpassword.errors.pattern"> Password & Confirm Password does not match.</div>
</div>
</div>
I did it like this. hope this will help you.
HTML :
<form [formGroup]='addAdminForm'>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Password</label>
<div class="col-sm-7">
<input type="password" class="form-control" formControlName='password' (keyup)="checkPassSame()">
<div *ngIf="addAdminForm.controls?.password?.invalid && addAdminForm.controls?.password.touched">
<p *ngIf="addAdminForm.controls?.password?.errors.required" class="errorMsg">*This field is required.</p>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Confirm Password</label>
<div class="col-sm-7">
<input type="password" class="form-control" formControlName='confPass' (keyup)="checkPassSame()">
<div *ngIf="addAdminForm.controls?.confPass?.invalid && addAdminForm.controls?.confPass.touched">
<p *ngIf="addAdminForm.controls?.confPass?.errors.required" class="errorMsg">*This field is required.</p>
</div>
<div *ngIf="passmsg != '' && !addAdminForm.controls?.confPass?.errors?.required">
<p class="errorMsg">*{{passmsg}}</p>
</div>
</div>
</div>
</form>
TS File :
export class AddAdminAccountsComponent implements OnInit {
addAdminForm: FormGroup;
password: FormControl;
confPass: FormControl;
passmsg: string;
constructor(
private http: HttpClient,
private router: Router,
) {
}
ngOnInit() {
this.createFormGroup();
}
// |---------------------------------------------------------------------------------------
// |------------------------ form initialization -------------------------
// |---------------------------------------------------------------------------------------
createFormGroup() {
this.addAdminForm = new FormGroup({
password: new FormControl('', [Validators.required]),
confPass: new FormControl('', [Validators.required]),
})
}
// |---------------------------------------------------------------------------------------
// |------------------------ Check method for password and conf password same or not -------------------------
// |---------------------------------------------------------------------------------------
checkPassSame() {
let pass = this.addAdminForm.value.password;
let passConf = this.addAdminForm.value.confPass;
if(pass == passConf && this.addAdminForm.valid === true) {
this.passmsg = "";
return false;
}else {
this.passmsg = "Password did not match.";
return true;
}
}
}
In case you have more than just Password and Verify Password fields. Like this the Confirm password field will only highlights error when user write something on this field:
validators.ts
import { FormGroup, FormControl, Validators, FormBuilder, FormGroupDirective, NgForm } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
export const EmailValidation = [Validators.required, Validators.email];
export const PasswordValidation = [
Validators.required,
Validators.minLength(6),
Validators.maxLength(24),
];
export class RepeatPasswordEStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return (control && control.parent.get('password').value !== control.parent.get('passwordAgain').value && control.dirty)
}
}
export function RepeatPasswordValidator(group: FormGroup) {
const password = group.controls.password.value;
const passwordConfirmation = group.controls.passwordAgain.value;
return password === passwordConfirmation ? null : { passwordsNotEqual: true }
}
register.component.ts
import { FormGroup, FormControl, Validators, FormBuilder} from '@angular/forms';
import { EmailValidation, PasswordValidation, RepeatPasswordEStateMatcher, RepeatPasswordValidator } from 'validators';
...
form: any;
passwordsMatcher = new RepeatPasswordEStateMatcher;
constructor(private formBuilder: FormBuilder) {
this.form = this.formBuilder.group ( {
email: new FormControl('', EmailValidation),
password: new FormControl('', PasswordValidation),
passwordAgain: new FormControl(''),
acceptTerms: new FormControl('', [Validators.requiredTrue])
}, { validator: RepeatPasswordValidator });
}
...
register.component.html
<form [formGroup]="form" (ngSubmit)="submitAccount(form)">
<div class="form-content">
<div class="form-field">
<mat-form-field>
<input matInput formControlName="email" placeholder="Email">
<mat-error *ngIf="form.get('email').hasError('required')">
E-mail is mandatory.
</mat-error>
<mat-error *ngIf="form.get('email').hasError('email')">
Incorrect E-mail.
</mat-error>
</mat-form-field>
</div>
<div class="form-field">
<mat-form-field>
<input matInput formControlName="password" placeholder="Password" type="password">
<mat-hint class="ac-form-field-description">Between 6 and 24 characters.</mat-hint>
<mat-error *ngIf="form.get('password').hasError('required')">
Password is mandatory.
</mat-error>
<mat-error *ngIf="form.get('password').hasError('minlength')">
Password with less than 6 characters.
</mat-error>
<mat-error *ngIf="form.get('password').hasError('maxlength')">
Password with more than 24 characters.
</mat-error>
</mat-form-field>
</div>
<div class="form-field">
<mat-form-field>
<input matInput formControlName="passwordAgain" placeholder="Confirm the password" type="password" [errorStateMatcher]="passwordsMatcher">
<mat-error *ngIf="form.hasError('passwordsNotEqual')" >Passwords are different. They should be equal!</mat-error>
</mat-form-field>
</div>
<div class="form-field">
<mat-checkbox name="acceptTerms" formControlName="acceptTerms">I accept terms and conditions</mat-checkbox>
</div>
</div>
<div class="form-bottom">
<button mat-raised-button [disabled]="!form.valid">Create Account</button>
</div>
</form>
i hope it helps!
Source: Stackoverflow.com