Angular Deep Dive into CanDeactivate Route Guard

Piyush Jain
5 min readMar 24, 2022

--

Angular Route Guard — CanDeactivate

What is CanDeactivate

In this article, we’ll learn about the CanDeactivate router guard. Router guard is a browser event that's used to warn users that they're about to leave a page.

You can use the CanDeactivate guard to prevent users from accidentally leaving a route/page in your application. For example if such page contains a text editor with unsaved changes or an unsubmitted form. You can let the user know they can’t leave the component yet by using a confirmation or alert before the navigation to the other route takes place using the Angular CanDeactivate route guard.

In this article, you’ll see how to use the Angular CanDeactivate guard by example. CanDeactivate is a TypeScript interface that needs to be implemented by a component to create a route guard. This guard will be used by the router to decide if the route can be deactivated. It can be implemented in any Angular component using the canDeactivate method of the interface.

Let’s see how to implement CanDeactivate step by step with Angular 10.

Step 1 — Creating the CanDeactivate Route Guard Service

ng generate guard guards/can-deactivate

Now your default can-deactivate.guard.ts will look like below.

@Injectable({
providedIn: 'root'
})
export class CanDeactivateGuard implements CanActivate {
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

return true;
}
}

We will modify this code.

firstly add CanComponentDeactive interface and type

type CanDeactivateType = Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;export interface CanComponentDeactivate {
canDeactivate: () => CanDeactivateType;
}

We simply implement the CanDeactivate interface with a generic component type that defines a canDeactivate method. The CanDeactivate interface contains one canDeactivate() method that you need to override and pass the generic component as parameter so we can call the canDeactivate method of of the component.

Step 2: Implement the canDeactivate() Method in your Component

Next, we need to implement the canDeactivate() method in our component, so first let's create the component.

Head back to your terminal and run the following command:

ng generate component components/user-details

add given code in your user-details.component.ts In this code we are creating a simple form which has 2 field for “name” and “email”.

Created canDeactivate method which will be executed whenever user will try to leave page.

export class UserDetailsComponent implements OnInit {
isSaved = false;
userDetailsForm: FormGroup;
constructor() {
this.userDetailsForm = new FormGroup({
name: new FormControl(''),
email: new FormControl(''),
});
}
ngOnInit(): void {
}
canDeactivate(): Observable<boolean> {
if (!this.isSaved) {
const result = window.confirm('There are unsaved changes! Are you sure?');
return of(result);
}
return of(true);
}
onSubmit() {
this.isSaved = true;
}
}

Add below code in your user-details.component.html . In this code we are have created a simple reactive form.

<h2>User Details</h2><form [formGroup]="userDetailsForm" novalidate (submit)="onSubmit()">
<div>
<label>Name</label>
<input type="text" [formControlName]="'name'" />
</div>
<div>
<label>Email</label>
<input type="email" [formControlName]="'email'" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>

Now add routing in your app-routing.module.ts under routes array.

{
path: '',
component: UserDetailsComponent,
canDeactivate: [CanDeactivateGuard]
},

Add this in app.component.html

<router-outlet></router-outlet>

Create another component using given command.

ng generate component components/page-one

Add new route in app-routing.module.ts under routes array

{
path: 'page-one',
component: PageOneComponent
}

Add link for page one in app.component.html

<a [routerLink]="['/page-one']">Page One</a>

If everything goes fine your page should look like below.

Now if you enter data in field and try to move on another page by clicking on page-one link without saving / submitting, it will show a confirmation message like below.

Note: CanDeactivate won’t work if user refreshes page, try to move back/forward using browser’s next and previous button or closes browser window/tab.

Solution is you can listen to window beforeunload event using @HostListener('window:beforeunload') .

The window beforeunload event activates (due to the @HostListener('window:beforeunload') ) and handles the confirm dialog on its own, without the CanDeactivate guard. In this case, Chrome and Firefox display their own pending changes warning message.

@HostListener('window:beforeunload', ['$event'])
public onBeforeUnload(event: BeforeUnloadEvent): void {
this.isAllowedNavigation(true).subscribe(isAllowedNavigation =>
{
if (event && !isAllowedNavigation) {
event.preventDefault();
event.returnValue = false;
}
});
}
canDeactivate(): Observable<boolean> {
return this.isAllowedNavigation();
}
private isAllowedNavigation(beforeunloadEvent = false): Observable<boolean> {
if (!this.isSaved || beforeunloadEvent) {
const result = window.confirm('There are unsaved changes! Are you sure?');
return of(result);
}
return of(true);
}

Source Code: https://github.com/piyush303/angular-candeactivate-guard-example

You can play with code here — https://stackblitz.com/edit/angular-ivy-nntfkd

If you liked this please give a clap.

Also let us know how you are doing this in your project. Do share if you know any other approach.

Read Next

References

--

--

Piyush Jain

An avid front-end technologist with 9+ years of web industry experience. Self-motivated professional with broad technical skill-set and very strong attention.