Using CanDeactivate Guard in Angular

Introduction to CanDeactivate Guard

In Angular, guards are special services that control access to routes. They can prevent unauthorized access, redirect users, or confirm navigation actions. Among these, the CanDeactivate guard is particularly useful for preventing users from leaving a route when they have unsaved changes or incomplete actions.

The CanDeactivate guard allows developers to prompt users with a confirmation dialog, ensuring that they don’t accidentally lose data or leave the page without saving important information.

1. Understanding CanDeactivate

The CanDeactivate guard is a type of route guard that determines whether a route can be exited. Unlike CanActivate, which controls entry into a route, CanDeactivate controls exit behavior.

It is commonly used in forms, editors, or any component where the user might make changes that are not yet saved.


2. How CanDeactivate Works

  1. You implement a service that implements the CanDeactivate interface.
  2. The guard receives the component instance as a parameter.
  3. You check a condition (like whether changes are saved).
  4. The guard returns true (allow navigation) or false (prevent navigation).
  5. Optionally, you can show a confirmation dialog to the user before leaving.

3. Basic Example

Suppose you have an editable component EditableComponent with a flag isSaved that indicates whether changes are saved.

unsaved-changes.guard.ts

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { EditableComponent } from './editable.component';

@Injectable({ providedIn: 'root' })
export class UnsavedChangesGuard implements CanDeactivate<EditableComponent> {
  canDeactivate(component: EditableComponent): boolean {
return component.isSaved || confirm('You have unsaved changes. Do you really want to leave?');
} }

In this example:

  • If component.isSaved is true, navigation proceeds.
  • If component.isSaved is false, a confirmation dialog is shown.
  • The user can either confirm to leave or cancel navigation.

4. Applying CanDeactivate to Routes

Once the guard is created, apply it to the relevant routes in your routing module.

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EditableComponent } from './editable/editable.component';
import { UnsavedChangesGuard } from './guards/unsaved-changes.guard';

const routes: Routes = [
  {
path: 'edit',
component: EditableComponent,
canDeactivate: &#91;UnsavedChangesGuard]
}, { path: '', redirectTo: '/edit', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}

5. Editable Component Example

editable.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-editable',
  template: `
&lt;h2&gt;Edit Content&lt;/h2&gt;
&lt;textarea &#91;(ngModel)]="content"&gt;&lt;/textarea&gt;
&lt;button (click)="save()"&gt;Save&lt;/button&gt;
` }) export class EditableComponent { content: string = ''; isSaved: boolean = false; save() {
this.isSaved = true;
alert('Changes saved!');
} }

Here, the user edits content in a textarea. If they try to navigate away without clicking Save, the CanDeactivate guard will prompt a confirmation dialog.


6. CanDeactivate with Async Confirmation

Sometimes, confirmation dialogs may be asynchronous (e.g., using a modal component). The guard can return a Promise or an Observable.

unsaved-changes.guard.ts

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { EditableComponent } from './editable.component';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UnsavedChangesGuard implements CanDeactivate<EditableComponent> {
  canDeactivate(
component: EditableComponent
): boolean | Observable<boolean> | Promise<boolean> {
if (component.isSaved) {
  return true;
}
return new Promise(resolve =&gt; {
  const confirmation = confirm('You have unsaved changes. Leave anyway?');
  resolve(confirmation);
});
} }

This approach is useful if you integrate a custom modal dialog that returns a Promise.


7. Using CanDeactivate with Multiple Components

You can use the same guard for multiple components by defining a common interface that all editable components implement.

can-component-deactivate.interface.ts

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

editable.component.ts

import { CanComponentDeactivate } from './can-component-deactivate.interface';
import { Observable } from 'rxjs';

export class EditableComponent implements CanComponentDeactivate {
  isSaved: boolean = false;

  canDeactivate(): boolean | Observable<boolean> | Promise<boolean> {
return this.isSaved || confirm('You have unsaved changes. Leave?');
} }

unsaved-changes.guard.ts

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { CanComponentDeactivate } from './can-component-deactivate.interface';

@Injectable({ providedIn: 'root' })
export class UnsavedChangesGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
} }

This makes the guard reusable across multiple components without repeating logic.


8. Combining CanDeactivate with Other Guards

Angular allows combining multiple guards on a single route. For example, you can use CanActivate to prevent unauthorized access and CanDeactivate to prevent unsaved changes.

{
  path: 'edit',
  component: EditableComponent,
  canActivate: [AuthGuard],
  canDeactivate: [UnsavedChangesGuard]
}

9. Best Practices for CanDeactivate Guards

  1. Use a Common Interface
    • Makes the guard reusable for multiple components.
  2. Return Observables/Promises for Async Dialogs
    • Integrates seamlessly with modals or third-party confirmation libraries.
  3. Avoid Blocking UI
    • Keep confirmation dialogs user-friendly and non-intrusive.
  4. Combine with CanActivate
    • For comprehensive route protection and data integrity.
  5. Test Thoroughly
    • Ensure unsaved changes are not lost during navigation.

10. Summary

The CanDeactivate guard is an essential tool in Angular for protecting users from losing unsaved changes. It provides:

  • A confirmation mechanism before leaving a route.
  • Integration with forms, editors, and any interactive components.
  • Support for synchronous and asynchronous confirmations.
  • Reusable architecture with interfaces for multiple components.

By implementing CanDeactivate, you can enhance user experience and ensure data integrity across your Angular application.


Example Recap

@Injectable({ providedIn: 'root' })
export class UnsavedChangesGuard implements CanDeactivate<EditableComponent> {
  canDeactivate(component: EditableComponent): boolean {
return component.isSaved || confirm('Discard changes?');
} } // Route configuration { path: 'edit', component: EditableComponent, canDeactivate: [UnsavedChangesGuard] }

With this setup, Angular ensures users are prompted whenever they try to leave a route with unsaved changes, making your application safer and more user-friendly.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *