In this 2nd part of our tutorial, we are going to continue our journey of creating reset password functionality. In the 1st part, we completed the back-end with Node.js Express and Node mailer, created an API which will use in this part in Angular. Let’s get started.
Let’s create a new project and run it.
ng new angular-reset-password
cd angular-reset-password
ng serve
We are going to have several pages so need to create an app-routing.module.ts for navigating needed pages in our site,
import { NgModule } from '@angular/core';
import{Routes, RouterModule} from '@angular/router';
import { SigninComponent } from './components/signin/signin.component';
import { SignupComponent } from './components/signup/signup.component';
import { RequestResetComponent } from './components/request-reset/request-reset.component';
import { ResponseResetComponent } from './components/response-reset/response-reset.component';
const routes: Routes = [
{
path: 'sign-in',
component: SigninComponent,
},
{
path: 'sign-up',
component: SignupComponent,
},
{
path: 'request-reset-password',
component: RequestResetComponent,
},
{
path: 'response-reset-password/:token',
component: ResponseResetComponent
},
{
path: '**',
redirectTo: 'sign-in'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class MainRoutingModule {}
The names of each rout already makes it easier to understand which rout for what should be used. You may also notice that
response-reset-password
includes a :token
That's the
resettoken
that we send via email in the back-end attached to the URL.ng generate service auth
Let’s update our service file:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
const BASEURL = 'http://localhost:3000/api/resetpassword';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) { }
registerUser(body): Observable<any> {
return this.http.post(`${BASEURL}/register`, body);
}
loginUser(body): Observable<any> {
return this.http.post(`${BASEURL}/login`, body);
}
requestReset(body): Observable<any> {
return this.http.post(`${BASEURL}/req-reset-password`, body);
}
newPassword(body): Observable<any> {
return this.http.post(`${BASEURL}/new-password`, body);
}
ValidPasswordToken(body): Observable<any> {
return this.http.post(`${BASEURL}/valid-password-token`, body);
}
}
The last
ValidPasswordToken
is for validating the token and checking if it’s still active. Other ones looks very descriptive. It’s time to create the components.It’s time to make Signup component functional.
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['../signin/signin.component.css']
})
export class SignupComponent implements OnInit {
SignupForm: FormGroup;
forbiddenEmails: any;
errorMessage: string;
constructor(
private fb: FormBuilder,
private authService: AuthService,
private router: Router,
) {
this.buildSignupForm();
}
ngOnInit() {
}
private buildSignupForm() {
this.SignupForm = this.fb.group({
username: [null, [Validators.required]],
email: [null, [Validators.required, Validators.email], this.forbiddenEmails],
password: [null, [Validators.required, Validators.minLength(4)]],
});
}
onSubmit() {
this.SignupForm.reset();
}
signupUser() {
this.authService.registerUser(this.SignupForm.value).subscribe(
data => {
this.SignupForm.reset();
setTimeout(() => {
this.router.navigate(['sign-in']);
}, 3000);
},
err => {
if (err.error.msg) {
this.errorMessage = err.error.msg[0].message;
}
if (err.error.message) {
this.errorMessage = err.error.message;
}
}
);
}
}
As you see it’s just a simple reactive form supported by Angular. We also created
SignupUser()
function for sending the data into the server and creating the user. Let’s create an HTML template for this component.<div class="container-fluid form">
<div class="row form-row ">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card my-5">
<div class="card-body">
<h5 class="card-title text-center">Sign Up</h5>
<div>
<div id="errorMsg" *ngIf="errorMessage">
<span>{{errorMessage}}</span>
</div>
<form action="" [formGroup]="SignupForm" (ngSubmit)="signupUser()">
<div class="form-group form-signup">
<input _ngcontent-c0="" class="form-control form-control-lg" placeholder="Username"
type="text" id="username" formControlName="username" />
<span *ngIf="!SignupForm.get('username').valid && SignupForm.get('username').touched"
class="help-block">Please enter a valid username!</span>
</div>
<div class="form-group">
<input _ngcontent-c0="" class="form-control form-control-lg" placeholder="E-mail" type="text"
id="email" formControlName="email" />
<span *ngIf="!SignupForm.get('email').valid && SignupForm.get('email').touched" class="help-block">
Please
enter a valid email!
</span>
</div>
<div class="form-group">
<input class="form-control form-control-lg" placeholder="Password" type="password"
formControlName="password" />
<span *ngIf="!SignupForm.get('password').valid && SignupForm.get('password').touched"
class="help-block">Password is required and must have more than 4 characters!</span>
</div>
<div class="form-group">
<button type="submit" class=" btn btn-primary" (ngSubmit)="signupUser()">Sign Up</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
It will look like this:
You can style it as you wish. This tutorial is mainly about functional part and we will not spend time on CSS.
That’s it. Let's create a Forgot password component where we should submit our email for receiving a link in our inbox containing a link to new page where we can change our password.
Nothing special will be in this component. We just will create another Angular reactive form that will check if the email is valid and then will use RequestResetUser
()
function for sending the data.import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormControl } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-request-reset',
templateUrl: './request-reset.component.html',
})
export class RequestResetComponent implements OnInit {
RequestResetForm: FormGroup;
forbiddenEmails: any;
errorMessage: string;
successMessage: string;
IsvalidForm = true;
constructor(
private authService: AuthService,
private router: Router,
) {
}
ngOnInit() {
this.RequestResetForm = new FormGroup({
'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails),
});
}
RequestResetUser(form) {
console.log(form)
if (form.valid) {
this.IsvalidForm = true;
this.authService.requestReset(this.RequestResetForm.value).subscribe(
data => {
this.RequestResetForm.reset();
this.successMessage = "Reset password link send to email sucessfully.";
setTimeout(() => {
this.successMessage = null;
this.router.navigate(['sign-in']);
}, 3000);
},
err => {
if (err.error.message) {
this.errorMessage = err.error.message;
}
}
);
} else {
this.IsvalidForm = false;
}
}
}
HTML template of this component will be almost the same as for
SignUp
component. <div class="container-fluid form">
<div class="row form-row ">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card my-5">
<div class="card-body">
<h5 class="card-title text-center">Forgot Password</h5>
<div>
<div id="errorMsg" *ngIf="errorMessage">
<span>{{errorMessage}}</span>
</div>
<div id="successMsg" *ngIf="successMessage">
<span>{{successMessage}}</span>
</div>
<form action="" [formGroup]="RequestResetForm" (ngSubmit)="RequestResetUser(RequestResetForm)">
<div class="form-group">
<input _ngcontent-c0="" class="form-control form-control-lg" placeholder="email"
type="text" id="email" formControlName="email" />
<span *ngIf="!RequestResetForm.get('email').valid && !IsvalidForm"
class="help-block">Please enter a valid email!</span>
</div>
<div class="form-group">
<div>
<button type="submit" class=" btn btn-primary">Reset
Password</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
Here is how will it look like:
The last component that we are going to build is
ResponseReset
component.Creating ResponseReset Component
This page will be navigated from your email that includes the URL with token. Let’s get started:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-request-reset',
templateUrl: './response-reset.component.html',
})
export class ResponseResetComponent implements OnInit {
ResponseResetForm: FormGroup;
errorMessage: string;
successMessage: string;
resetToken: null;
CurrentState: any;
IsResetFormValid = true;
constructor(
private authService: AuthService,
private router: Router,
private route: ActivatedRoute,
private fb: FormBuilder ) {
this.CurrentState = 'Wait';
this.route.params.subscribe(params => {
this.resetToken = params.token;
console.log(this.resetToken);
this.VerifyToken();
});
}
ngOnInit() {
this.Init();
}
VerifyToken() {
this.authService.ValidPasswordToken({ resettoken: this.resetToken }).subscribe(
data => {
this.CurrentState = 'Verified';
},
err => {
this.CurrentState = 'NotVerified';
}
);
}
Init() {
this.ResponseResetForm = this.fb.group(
{
resettoken: [this.resetToken],
newPassword: ['', [Validators.required, Validators.minLength(4)]],
confirmPassword: ['', [Validators.required, Validators.minLength(4)]]
}
);
}
Validate(passwordFormGroup: FormGroup) {
const new_password = passwordFormGroup.controls.newPassword.value;
const confirm_password = passwordFormGroup.controls.confirmPassword.value;
if (confirm_password.length <= 0) {
return null;
}
if (confirm_password !== new_password) {
return {
doesNotMatch: true
};
}
return null;
}
ResetPassword(form) {
console.log(form.get('confirmPassword'));
if (form.valid) {
this.IsResetFormValid = true;
this.authService.newPassword(this.ResponseResetForm.value).subscribe(
data => {
this.ResponseResetForm.reset();
this.successMessage = data.message;
setTimeout(() => {
this.successMessage = null;
this.router.navigate(['sign-in']);
}, 3000);
},
err => {
if (err.error.message) {
this.errorMessage = err.error.message;
}
}
);
} else { this.IsResetFormValid = false; }
}
}
Firstly it verifies the token then just compares 2 passwords to see if they match and as the last step it sends the updated password to the back-end. That’s it! We can create a HTML template for it.
<div class="container-fluid form">
<div class="row" *ngIf="CurrentState=='Wait'">
<div class="col-md-12 close-form">
<h2> Please Wait...</h2>
</div>
</div>
<div class="row"
*ngIf="CurrentState=='NotVerified'">
<div class="col-md-12">
<h2> Invalid URL.</h2>
</div>
</div>
<div class="row" *ngIf="CurrentState=='Verified'">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card card-signin my-5">
<div class="card-body">
<h5 class="card-title text-center">Set New Password</h5>
<div>
<div id="errorMsg" *ngIf="errorMessage">
<span>{{errorMessage}}</span>
</div>
<div id="successMsg" *ngIf="successMessage">
<span>{{successMessage}}</span>
</div>
<form action="" [formGroup]="ResponseResetForm" (ngSubmit)="ResetPassword(ResponseResetForm)">
<div class="form-group">
<input _ngcontent-c0="" class="form-control form-control-lg" placeholder="Password"
type="password" id="password" formControlName="newPassword" />
<span *ngIf="!ResponseResetForm.get('newPassword').valid && !IsResetFormValid"
class="help-block">Password is required with atleast 4 characters.</span>
</div>
<div class="form-group">
<input _ngcontent-c0="" class="form-control form-control-lg"
placeholder="Confirm Password" type="password" id="cpassword"
formControlName="confirmPassword" />
<span *ngIf="!ResponseResetForm.get('confirmPassword').valid && !IsResetFormValid"
class="help-block">. </span>
<span
*ngIf="ResponseResetForm.get('confirmPassword').valid && (ResponseResetForm.get('confirmPassword').value != ResponseResetForm.get('newPassword').value) && !IsResetFormValid"
class="help-block">Confirm Password does not match with password.</span>
</div>
<div class="form-group">
<div>
<button type="submit" class=" btn btn-primary">Update
Password</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
Based on condition it shows the right part of the template.
Here is how it will look like:
That’s all! Hope this tutorial was helpful for you.
Also don’t forget to include components in app.module.ts
You can get the full source code here:
Github