Angular Forms Confirming Passwords (Learn it in 18 Steps)

Srikanth
11 min readJan 9, 2023

Please find the below steps to implement Angular material Forms confirming passwords.

Photo by Mourizal Zativa on Unsplash

0. Before starting the application, you need to know about Reactive Forms in angular. If you know about forms you can ignore this step.

Basically, We are using forms for taking the inputs from the user and storing those inputs inside the database, In that way, we can store the information permanently. For taking the inputs from the user we need to create a form at the UI level.

We have a two-way data binding concept in side form syntax to save the details in angular .ts file and sent it back to the server by using ajax calls, But it is not a good way. So in angular, we have two approaches to implement forms and form validations.

Those are two approaches inside reactive forms,

  • Template Driven Forms
  • Reactive Forms

But in the Template-driven forms approach, we will write most of the code in Html. But in this approach, if you want to do any implementations most of them we will write inside an Html file(validations etc.).

And in the Reactive Forms approach, we will write the logic at .ts file ( Creating form groups, form controls, and validations, etc. )and it is an industry-level approach (Meaning most of the industries will follow this approach only).

In Reactive Forms, by using Form builder we can create forms and In Form Builder, we have form groups, form controls, and form arrays. By combining form groups, Form arrays and form controls we will create the forms ( Here form controls are like input fields ).

I think you understood something, In the below example, I am going to implement confirming two passwords by using custom validator and error state matcher.

Here Error state matcher is used to throw an error message to the form fields by creating error state matcher object and add it to the form filed as a property.

For more information, please visit the official angular.io website.

1.Create an angular project with the below command.

ng angular-material-forms-confirm-passwords

2. After successful creation of an angular app, change the file directory to project-name. “cd angular-material-forms-confirm-passwords”.

Open the project in vs code using “code .” in terminal or open with vs code. Then run the project using “ng serve” in a terminal. Open project in chrome using localhost:4200

3. Open the app component in vs code and remove the content which is created by angular CLI while creating the app. For Adding angular material using the command

"ng add @angular/material"

4. Select theme, am selecting Indigo/Pink, and click the below items as “yes

  • Set up global Angular Material typography styles? Am selecting as y
  • Set up browser animations for Angular Material? (Y/n) Select ‘y’.

5. Created Shared Module in the libs folder using “ng generate module shared”. And import, export material modules in “shared.module.ts”. And also add in the app.module.ts

  • After that add bootstrap CSS inside index.html file as below if you know how to add bootstrap in angular, please ignore this step.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular Material Forms</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>

6. Add “ReactiveFormsModule”, and “Forms Module” in app.module.ts as below

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent,
SignupComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
SharedModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

7. Create reset-password component under the apps/components folder. And add reset-component in router and add path as” reset-password”.

8. Open “reset-password.component.ts”, then add “formbuilder ” as a dependency in the constructor. Create a form variable above the constructor.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-reset-password',
templateUrl: './reset-password.component.html',
styleUrls: ['./reset-password.component.scss']
})
export class ResetPasswordComponent implements OnInit {
public form: FormGroup;

constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
}

}

9. Create formInit method to initialize the form and call the method from either constructor or ngOnInit.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-reset-password',
templateUrl: './reset-password.component.html',
styleUrls: ['./reset-password.component.scss']
})
export class ResetPasswordComponent implements OnInit {
public form: FormGroup;

constructor(private fb: FormBuilder) {
this.formInit();
}

ngOnInit(): void {
}

private formInit() {
}

}

10. And create a form group using form builder and add form controls. Form Controls like oldPassword”, “newPassword”, “confirmNewPassword.

private formInit() {
this.form = this.fb.group({
oldPassword: ['', [Validators.required]],
newPassword: ['', [Validators.required]],
confirmNewPassword: ['', [Validators.required]],
})
}

11. After that add the password pattern to new password and confirm new password form controls as a validation pattern.

Am taking the pattern that accepts at least one Uppercase, one lowercase, one number, one special character, min length 8 characters and max 16 characters length.

12. Create password pattern variable and add assign the regular expression value to that variable like below.

public passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{8,16}$/

13. After that add the pattern inside form as a form control validator as below.

private formInit() {
this.form = this.fb.group({
oldPassword: ['', [Validators.required]],
newPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
confirmNewPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
});
}

14. After that create custom validator method to check that condition and apply it to entire form. In that way if user changes the either newPassword or confirmNewPassword field that validator method will be called automatically.

  • create checking Passwords method and the logic to check new password and confirm new password is same or not. If same, it return false or else it will return {“notMatched”: true}
public checkingPasswords(formGroup: FormGroup) {
if (
formGroup.controls.newPassword.value &&
formGroup.controls.confirmNewPassword.value &&
formGroup.controls.newPassword.value &&
formGroup.controls.newPassword.value.length >= 8 &&
formGroup.controls.newPassword.value.length <= 16 &&
formGroup.controls.confirmNewPassword.value.length >= 8 &&
formGroup.controls.confirmNewPassword.value.length <= 16
) {
return formGroup.controls.newPassword.value === formGroup.controls.confirmNewPassword.value ? false : { "notMatched": true }
}
return false;
}
  • Add the custom validator method inside form control as a validator.
private formInit() {
this.form = this.fb.group({
oldPassword: ['', [Validators.required]],
newPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
confirmNewPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
}, { validator: this.checkingPasswords });
}

15. Open reset-form.component.html and add the code in the below way.

  • Add div and form tag in Html file.
<div class="container">
<form class="form shadow m-3 p-3" [formGroup]="form" (ngSubmit)="submitForm()">
<h1>Reset Password</h1>
</form>
</div>
  • Increase code by one form control and add validate message also.
<div class="container">
<form class="form shadow m-3 p-3" [formGroup]="form" (ngSubmit)="submitForm()">
<h1>Reset Password</h1>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Old Password</mat-label>
<input type="password" matInput formControlName="oldPassword" placeholder="Enter Old Password">
<mat-error *ngIf="form.controls.oldPassword.hasError('required')">
Old Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
</form>
</div>
  • Add all the controls and form validation messages.
<div class="container">
<form class="form shadow m-3 p-3" [formGroup]="form" (ngSubmit)="submitForm()">
<h1>Reset Password</h1>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Old Password</mat-label>
<input type="password" matInput formControlName="oldPassword" placeholder="Enter Old Password">
<mat-error *ngIf="form.controls.oldPassword.hasError('required')">
Old Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>New Password</mat-label>
<input type="password" matInput formControlName="newPassword" placeholder="Enter New Password">
<mat-error *ngIf="form.controls.newPassword.hasError('required')">
New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Confirm New Password</mat-label>
<input type="password" matInput formControlName="confirmNewPassword" placeholder="Enter Confirm New Password"
[errorStateMatcher]="matcher"
>
<mat-error *ngIf="form.controls.confirmNewPassword.hasError('required')">
Confirm New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3">
<button type="submit" mat-raised-button color="primary" class="m-2" style="width: 150px;" [disabled]="!form.valid">Update Password</button>
<button type="button" mat-raised-button color="warn" class="m-2" style="width: 100px;" (click)="form.reset()">Reset</button>
</div>
</form>
</div>

12. After successfully adding the Html code and checking the changes in the browser. Please add the submit method in reset-form.component.ts.

public submitForm(){
console.log(this.form);
console.log(this.form.getRawValue())
}

13. Test the form by giving all the forms and clicking on submit. You can able to see the values in the console.

14. If you observe while typing the letters, we need to show the errors like “password contains at least one upper case, one lower case, special character and min length and max length messages”.

We need to highlight those messages as green and red while typing. Here red means warning and green means success.

For achieving that create one method to check the errors and also call that method inside html to show in browser.

  • Add check validations method inside reset-password.component.ts
checkValidations(control, type) {
switch (type) {
case 'special-character':
return /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(control.value);;
case 'number':
return /\d/.test(control.value);
case 'lowercase':
return /[a-z]/.test(control.value);
case 'uppercase':
return /[A-Z]/.test(control.value);
case 'length':
return control.value.length >= 8 && control.value.length <= 16;
default:
return false
}
}
  • After that call this method from template below the newPassword and confirmNewPassword form controls.
<div class="container">
<form class="form shadow m-3 p-3" [formGroup]="form" (ngSubmit)="submitForm()">
<h1>Reset Password</h1>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Old Password</mat-label>
<input type="password" matInput formControlName="oldPassword" placeholder="Enter Old Password">
<mat-error *ngIf="form.controls.oldPassword.hasError('required')">
Old Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>New Password</mat-label>
<input type="password" matInput formControlName="newPassword" placeholder="Enter New Password"
>
<mat-error *ngIf="form.controls.newPassword.hasError('required')">
New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3" *ngIf="form.controls.newPassword.hasError('pattern')">
<mat-error *ngIf="form.controls.newPassword.hasError('pattern')">
<span class="danger">Password Must contain below conditions.</span>
<ul>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'uppercase'), 'danger': !checkValidations(form.controls.newPassword,'uppercase')}">Min 1 Uppercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'lowercase'), 'danger': !checkValidations(form.controls.newPassword,'lowercase')}">Min 1 Lowercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'special-character'), 'danger': !checkValidations(form.controls.newPassword,'special-character')}">Min 1 Special Character.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'number'), 'danger': !checkValidations(form.controls.newPassword,'number')}">Min 1 Number.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'length'), 'danger': !checkValidations(form.controls.newPassword,'length')}">Min 8 Characters.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'length'), 'danger': !checkValidations(form.controls.newPassword,'length')}">Max 16 Characters.</li>
</ul>
</mat-error>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Confirm New Password</mat-label>
<input type="password" matInput formControlName="confirmNewPassword" placeholder="Enter Confirm New Password"
>
<mat-error *ngIf="form.controls.confirmNewPassword.hasError('required')">
Confirm New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3" *ngIf="form.controls.confirmNewPassword.hasError('pattern')">
<mat-error *ngIf="form.controls.confirmNewPassword.hasError('pattern')">
<span class="danger">Password Must contain below conditions.</span>
<ul>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'uppercase'), 'danger': !checkValidations(form.controls.confirmNewPassword,'uppercase')}">Min 1 Uppercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'lowercase'), 'danger': !checkValidations(form.controls.confirmNewPassword,'lowercase')}">Min 1 Lowercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'special-character'), 'danger': !checkValidations(form.controls.confirmNewPassword,'special-character')}">Min 1 Special Character.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'number'), 'danger': !checkValidations(form.controls.confirmNewPassword,'number')}">Min 1 Number.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'length'), 'danger': !checkValidations(form.controls.confirmNewPassword,'length')}">Min 8 Characters.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'length'), 'danger': !checkValidations(form.controls.confirmNewPassword,'length')}">Max 16 Characters.</li>
</ul>
</mat-error>
</div>
<div class="row mt-3">
<button type="submit" mat-raised-button color="primary" class="m-2" style="width: 150px;" [disabled]="!form.valid">Update Password</button>
<button type="button" mat-raised-button color="warn" class="m-2" style="width: 100px;" (click)="form.reset()">Reset</button>
</div>
</form>
</div>
  • Check the changes inside the browser by using localhost:4200.

15. But if you observed, if the newPassword and confirmedNewPassword are not matched the button is disabled and validation message is not showing. To solve this problem there is one concept is there “Error State Matcher”.

if matches that update password button should be enabled.

Using Error State Matcher, we can solve this problem and we can show the error message as “Passwords are not matched”. Also, the input fields are showing red borders.

16. Create custom Error State Matcher class on the top of reset-password component class and also create the matcher variable and assign matcher object to that variable.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return !!(control && control.valid && (control.dirty || control.touched) && form.hasError('notMatched'));
}
}

@Component({
selector: 'app-reset-password',
templateUrl: './reset-password.component.html',
styleUrls: ['./reset-password.component.scss']
})
export class ResetPasswordComponent implements OnInit {
public form: FormGroup;
public passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{8,16}$/
public matcher = new MyErrorStateMatcher();

constructor(private fb: FormBuilder) {
this.formInit();
}

ngOnInit(): void {
}

private formInit() {
this.form = this.fb.group({
oldPassword: ['', [Validators.required]],
newPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
confirmNewPassword: ['', [Validators.required, Validators.pattern(this.passwordPattern)]],
}, { validator: this.checkingPasswords });
}

public submitForm() {
console.log(this.form);
console.log(this.form.getRawValue())
}

public checkingPasswords(formGroup: FormGroup) {
if (
formGroup.controls.newPassword.value &&
formGroup.controls.confirmNewPassword.value &&
formGroup.controls.newPassword.value &&
formGroup.controls.newPassword.value.length >= 8 &&
formGroup.controls.newPassword.value.length <= 16 &&
formGroup.controls.confirmNewPassword.value.length >= 8 &&
formGroup.controls.confirmNewPassword.value.length <= 16
) {
return formGroup.controls.newPassword.value === formGroup.controls.confirmNewPassword.value ? false : { "notMatched": true }
}
return false;
}

checkValidations(control, type) {
switch (type) {
case 'special-character':
return /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(control.value);;
case 'number':
return /\d/.test(control.value);
case 'lowercase':
return /[a-z]/.test(control.value);
case 'uppercase':
return /[A-Z]/.test(control.value);
case 'length':
return control.value.length >= 8 && control.value.length <= 16;
default:
return false
}
}

}

17. Add the error matcher inside the reset-form.component.html and also add the validation message stating that passwords are not matched.

<div class="container">
<form class="form shadow m-3 p-3" [formGroup]="form" (ngSubmit)="submitForm()">
<h1>Reset Password</h1>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Old Password</mat-label>
<input type="password" matInput formControlName="oldPassword" placeholder="Enter Old Password">
<mat-error *ngIf="form.controls.oldPassword.hasError('required')">
Old Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>New Password</mat-label>
<input type="password" matInput formControlName="newPassword" placeholder="Enter New Password"
[errorStateMatcher]="matcher"
>
<mat-error *ngIf="form.controls.newPassword.hasError('required')">
New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3" *ngIf="form.controls.newPassword.hasError('pattern')">
<mat-error *ngIf="form.controls.newPassword.hasError('pattern')">
<span class="danger">Password Must contain below conditions.</span>
<ul>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'uppercase'), 'danger': !checkValidations(form.controls.newPassword,'uppercase')}">Min 1 Uppercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'lowercase'), 'danger': !checkValidations(form.controls.newPassword,'lowercase')}">Min 1 Lowercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'special-character'), 'danger': !checkValidations(form.controls.newPassword,'special-character')}">Min 1 Special Character.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'number'), 'danger': !checkValidations(form.controls.newPassword,'number')}">Min 1 Number.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'length'), 'danger': !checkValidations(form.controls.newPassword,'length')}">Min 8 Characters.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.newPassword,'length'), 'danger': !checkValidations(form.controls.newPassword,'length')}">Max 16 Characters.</li>
</ul>
</mat-error>
</div>
<div class="row mt-3">
<mat-form-field class="col-md-6">
<mat-label>Confirm New Password</mat-label>
<input type="password" matInput formControlName="confirmNewPassword" placeholder="Enter Confirm New Password"
[errorStateMatcher]="matcher"
>
<mat-error *ngIf="form.controls.confirmNewPassword.hasError('required')">
Confirm New Password is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div class="row mt-3" *ngIf="form.controls.confirmNewPassword.hasError('pattern')">
<mat-error *ngIf="form.controls.confirmNewPassword.hasError('pattern')">
<span class="danger">Password Must contain below conditions.</span>
<ul>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'uppercase'), 'danger': !checkValidations(form.controls.confirmNewPassword,'uppercase')}">Min 1 Uppercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'lowercase'), 'danger': !checkValidations(form.controls.confirmNewPassword,'lowercase')}">Min 1 Lowercase Letter.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'special-character'), 'danger': !checkValidations(form.controls.confirmNewPassword,'special-character')}">Min 1 Special Character.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'number'), 'danger': !checkValidations(form.controls.confirmNewPassword,'number')}">Min 1 Number.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'length'), 'danger': !checkValidations(form.controls.confirmNewPassword,'length')}">Min 8 Characters.</li>
<li [ngClass]="{'text-success':checkValidations(form.controls.confirmNewPassword,'length'), 'danger': !checkValidations(form.controls.confirmNewPassword,'length')}">Max 16 Characters.</li>
</ul>
</mat-error>
</div>
<div class="row mt-1" *ngIf="form.hasError('notMatched')">
<mat-error *ngIf="form.hasError('notMatched')">
<span class="danger">Passwords are not matched.</span>
</mat-error>
</div>
<div class="row mt-3">
<button type="submit" mat-raised-button color="primary" class="m-2" style="width: 150px;" [disabled]="!form.valid">Update Password</button>
<button type="button" mat-raised-button color="warn" class="m-2" style="width: 100px;" (click)="form.reset()">Reset</button>
</div>
</form>
</div>

18. After all the steps are completed, please run the server by using ng serve and open it in browser by using localhost:4200. If something went wrong, please check it from the step no 1.

Output:

form with un touched form controls
passwords not matched
passwords with errors
valid form
Source Code
GitHub: https://github.com/mryenagandula/angular-material-forms-confirm-passwords

Stack blitz Project Preview: Angular Material Forms Confirm Passwords

Thanks for reading my article, please share your feedback, claps, and comments. In that way, it will have helped me to improve my articles in the future. Please share my story with your near and dear, also follow and subscribe to the medium.

--

--

Srikanth

Senior Software Engineer | Storyteller | Quick Learner | React JS | Angular | Java | Learn Something new from my stories