Hey guys, in this tutorial
we are going to see how to show a warning to the user if the user edited the data on the page and trying to leave the page without saving the changes. We are going to
use CanDeactivate guard to detect the user is leaving the page and then will check
for the change in data. So, let’s do it step by step.
We are going to use the same
project from our blog to do this task.
Following are the two
components from our previous post.
first-page.component.html
<div class="container">
<form #frm="ngForm" id="frm"><br><br>
<div class="row">
<div class="col-lg-4"></div>
<div class="col-lg-6">
<h3>Login</h3>
<table>
<thead>
<th>
Login Name
</th>
<th>
<input class="form-control" type="text" [(ngModel)]="user.userName" name="userName" required>
</th>
</thead>
<tbody>
<tr>
<th>
Password
</th>
<td><input class="form-control" type="password" [(ngModel)]="user.password" name="password"></td>
</tr>
<tr>
<td>
</td>
<td>
<button class="btn btn-primary">Login</button>
<button class="btn btn-light" (click)="registerMe();">Register Me</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
first-page.component.ts
import { Component, OnInit, HostListener, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { ThrowStmt } from '@angular/compiler';
import { NgForm } from '@angular/forms';
import { user } from '../user/user';
import { BaseComponent } from '../can-deactivate';
@Component({
selector: 'app-first-page',
templateUrl: './first-page.component.html',
styleUrls: ['./first-page.component.css']
})
export class FirstPageComponent implements OnInit {
@ViewChild('frm') public userFrm: NgForm;
private user = new user();
constructor(private router: Router) { }
ngOnInit() {
console.log("user : " + user.name);
}
registerMe() {
console.log("form touched " + this.userFrm.touched);
this.router.navigate(['/secondpage']);
}
}
second-page.component.html
<br><br>
<div class="row">
<div class="col-lg-4"></div>
<div class="col-lg-6">
<form action="" #frm="ngForm">
<h3>Registration</h3>
<table>
<tr>
<td>User name</td>
<td><input [(ngModel)]="user.userName" class="form-control" type="text" name="userName"></td>
</tr>
<tr>
<td>password</td>
<td><input [(ngModel)]="user.password" class="form-control" type="password" name="password"></td>
</tr>
<tr>
<td>confirm password</td>
<td><input class="form-control" type="password" name="password"></td>
</tr>
<tr>
<td>email</td>
<td><input [(ngModel)]="user.email" class="form-control" type="text" name="email"></td>
</tr>
<tr>
<td>contact No</td>
<td><input [(ngModel)]="user.contactNo" class="form-control" type="text" name="contactNo"></td>
</tr>
<tr>
<td>address</td>
<td><input [(ngModel)]="user.address" class="form-control" type="text" name="address"></td>
</tr>
<tr>
<td></td>
<td><button class="btn
btn-light" (click)="gotoLogin()">Cancel</button> <button class="btn
btn-primary">save</button></td>
</tr>
</table>
</form>
</div>
</div>
second-page.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { BaseComponent } from '../can-deactivate';
import { NgForm } from '@angular/forms';
import { user } from '../user/user';
@Component({
selector: 'app-second-page',
templateUrl: './second-page.component.html',
styleUrls: ['./second-page.component.css']
})
export class SecondPageComponent implements OnInit, BaseComponent {
@ViewChild('frm') public userFrm: NgForm;
private user = new user();
constructor(private router: Router) { }
ngOnInit() {
}
gotoLogin() {
this.router.navigate(['/firstpage']);
}
containsEditedData() {
return this.userFrm.dirty;
}
}
In the above HTML, we have created
login and registration pages respectively.
We are going to show a warning when the user is
going to fill something into the registration form and will going to leave without
completing the registration. To achieve that we don’t need many changes in the previous page, we are going to create a new guard and apply it on the registration
page.
At very first let’s create an interface and implement it by the component on which we are going to apply the
guard. We are creating interface because we need to make wrapper component so
we can make guard generic means guard can accept any component and also, we can
define some method in it to implement the basic logic.
can-deactivate.ts
export interface BaseComponent {
containsEditedData();
}
Commands to generate the
guard
Command: ng generate guard
guardname
Or shortened
Command: ng g g guardname
guard-unsaved-data.guard.ts
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { BaseComponent } from './can-deactivate';
@Injectable({
providedIn: 'root'
})
export class GuardUnsavedDataGuard implements CanDeactivate<BaseComponent> {
canDeactivate(component: BaseComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
if (component.containsEditedData()) {
if (confirm("please save the
changes! If you leave, your changes will be lost. say cancel to stay on page")) {
return true;
} else {
return false;
}
}
return true;
}
}
Here we have just accepted
the component in guard and called containsEditedData() function, which needs to
implement by component( that’s why we have created an interface so any component
can be passed to the guard with help of interface’s reference and one function
in that interface, so implementing class must have that functions
implementation) and it returns true if
there is something changed on-page means something is filled in registration
form. If something is filled, then show waring of edited data else it can be
deactivated.
Now everything is ready
but how to apply the guard, here it is
app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FirstPageComponent } from './first-page/first-page.component';
import { SecondPageComponent } from './second-page/second-page.component';
import { GuardUnsavedDataGuard } from './guard-unsaved-data.guard';
const routes: Routes = [
{ path: '', component: FirstPageComponent },
{ path: 'firstpage', component: FirstPageComponent },
{ path: 'secondpage', component: SecondPageComponent, canDeactivate: [GuardUnsavedDataGuard] }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [GuardUnsavedDataGuard]
})
export class AppRoutingModule { }
In our app.routing file while specifying the
routes we can apply a guard for particular component here we have applied the guard on the second component. Here we have applied guard of type deactivate, we
have two types of guards one is canActivate, which gets called before loading the
component and other is canDeactivate, which gets called before unloading the
component.
Here is the output
Yeeha… that’s all we have
done to implement the unsaved data guard.
Comments
Post a Comment