What is CanDeactivate in Angular?
What is CanDeactivate?
CanDeactivate is an Angular route guard used to determine whether a user is allowed to leave the current route/component.
It is commonly used when a user has unsaved changes in a form.
For example:
User editing Product
↓
Changes are not saved
↓
User clicks Home
↓
CanDeactivate Guard
↓
"Are you sure you want to leave?"
/ \
Yes No
↓ ↓
Leave Stay
In modern Angular, we commonly use:
CanDeactivateFn
Why Do We Need CanDeactivate?
Suppose a user is editing:
/products/edit/10
They change:
Product Name
Price
Description
but forget to click Save.
Then they click:
Home
Without CanDeactivate:
Edit Product
↓
Click Home
↓
Home Page
↓
Changes Lost
With CanDeactivate:
Edit Product
↓
Click Home
↓
CanDeactivate
↓
Unsaved changes?
↓
Yes
↓
"Are you sure?"
This helps prevent accidental data loss.
Full Simple Sample Program
We'll create an Edit Product page.
If the user changes the product name without saving, Angular asks for confirmation before leaving.
edit-product.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-edit-product',
standalone: true,
imports: [FormsModule],
template: `
<h2>Edit Product</h2>
<label>Product Name:</label>
<input
type="text"
[(ngModel)]="productName"
(ngModelChange)="onChange()">
<br><br>
<button (click)="save()">
Save
</button>
`
})
export class EditProduct {
productName = 'Laptop';
hasUnsavedChanges = false;
onChange(): void {
this.hasUnsavedChanges = true;
}
save(): void {
console.log('Product saved');
this.hasUnsavedChanges = false;
}
canLeave(): boolean {
if (!this.hasUnsavedChanges) {
return true;
}
return confirm(
'You have unsaved changes. Do you want to leave?'
);
}
}
unsaved-changes.guard.ts
import { CanDeactivateFn } from '@angular/router';
import { EditProduct } from './edit-product';
export const unsavedChangesGuard:
CanDeactivateFn<EditProduct> =
(component) => {
return component.canLeave();
};
home.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
standalone: true,
template: `
<h2>Home Page</h2>
`
})
export class Home {
}
app.routes.ts
import { Routes } from '@angular/router';
import { Home } from './home';
import { EditProduct } from './edit-product';
import { unsavedChangesGuard }
from './unsaved-changes.guard';
export const routes: Routes = [
{
path: '',
component: Home
},
{
path: 'products/edit/:id',
component: EditProduct,
canDeactivate: [
unsavedChangesGuard
]
}
];
app.ts
import { Component } from '@angular/core';
import {
RouterLink,
RouterOutlet
} from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterLink,
RouterOutlet
],
template: `
<h1>My Application</h1>
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/products/edit/10">
Edit Product
</a>
</nav>
<hr>
<router-outlet></router-outlet>
`
})
export class App {
}
app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
How Does It Work?
The important route configuration is:
{
path: 'products/edit/:id',
component: EditProduct,
canDeactivate: [
unsavedChangesGuard
]
}
This tells Angular:
Before leaving
EditProduct, executeunsavedChangesGuard.
Step 1: User Opens Edit Page
/products/edit/10
The component initially has:
hasUnsavedChanges = false;
Step 2: User Changes the Product
When the user changes the input:
(ngModelChange)="onChange()"
Angular executes:
onChange(): void {
this.hasUnsavedChanges = true;
}
Now:
hasUnsavedChanges = true
Step 3: User Tries to Leave
Suppose the user clicks:
Home
Angular does not immediately leave.
It first executes:
unsavedChangesGuard
The guard calls:
component.canLeave();
Step 4: Check Unsaved Changes
The component contains:
canLeave(): boolean {
if (!this.hasUnsavedChanges) {
return true;
}
return confirm(
'You have unsaved changes. Do you want to leave?'
);
}
If there are no changes:
return true;
Angular allows navigation.
If there are unsaved changes:
confirm(...)
is displayed.
Complete Flow
User opens Edit Product
↓
Changes product name
↓
hasUnsavedChanges = true
↓
Clicks Home
↓
CanDeactivate guard executes
↓
component.canLeave()
↓
Unsaved changes?
/ \
No Yes
↓ ↓
true confirm()
↓ / \
Leave OK Cancel
↓ ↓
true false
↓ ↓
Leave Stay
CanActivate vs CanDeactivate
This is an important interview distinction.
CanActivate |
CanDeactivate |
|---|---|
| Controls entering a route | Controls leaving a route |
| Runs before route activation | Runs before leaving current route |
| Common for authentication | Common for unsaved changes |
CanActivateFn |
CanDeactivateFn<T> |
| “Can I enter?” | “Can I leave?” |
Think:
CanActivate
↓
Can I ENTER?
CanDeactivate
↓
Can I LEAVE?
Understanding CanDeactivateFn<EditProduct>
Look at:
CanDeactivateFn<EditProduct>
The generic type:
<EditProduct>
tells Angular that this guard is associated with an EditProduct component.
Therefore, the guard receives the component instance:
(component) => {
and can access its properties and methods:
component.hasUnsavedChanges
component.canLeave()
This is one major difference from CanActivate.
CanDeactivate can inspect the component being left.
A Better Reusable Approach
In a larger application, you may have several forms:
Edit Product
Edit Customer
Edit Order
Edit Profile
Instead of making a guard depend specifically on EditProduct, define a common interface.
Interface
export interface CanComponentDeactivate {
canDeactivate(): boolean;
}
Component
export class EditProduct
implements CanComponentDeactivate {
hasUnsavedChanges = false;
canDeactivate(): boolean {
if (!this.hasUnsavedChanges) {
return true;
}
return confirm(
'You have unsaved changes. Do you want to leave?'
);
}
}
Guard
import { CanDeactivateFn } from '@angular/router';
import { CanComponentDeactivate }
from './can-component-deactivate';
export const unsavedChangesGuard:
CanDeactivateFn<CanComponentDeactivate> =
(component) => {
return component.canDeactivate();
};
Now the same guard can be reused for:
EditProduct
EditCustomer
EditOrder
EditProfile
This is a cleaner design for larger applications.
What Can CanDeactivate Return?
Like other Angular guards, it can return navigation decisions synchronously or asynchronously.
Allow
return true;
Means:
Allow the user to leave.
Block
return false;
Means:
Stay on the current page.
Redirect
A guard can also return a redirect result when appropriate.
It can additionally use:
Promise
Observable
for asynchronous checks.
Older Angular: Class-Based CanDeactivate
Older Angular applications commonly used a class-based guard.
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { EditProduct } from './edit-product';
@Injectable({
providedIn: 'root'
})
export class UnsavedChangesGuard
implements CanDeactivate<EditProduct> {
canDeactivate(
component: EditProduct
): boolean {
return component.canLeave();
}
}
Route:
{
path: 'products/edit/:id',
component: EditProduct,
canDeactivate: [
UnsavedChangesGuard
]
}
Modern Angular commonly uses the functional version:
export const unsavedChangesGuard:
CanDeactivateFn<EditProduct> =
(component) => {
return component.canLeave();
};
So:
Older Angular
↓
Class implements CanDeactivate
Modern Angular
↓
CanDeactivateFn<T>
↓
Functional Guard
Advantages
- Prevents accidental loss of unsaved data.
- Useful for forms and edit screens.
- Allows confirmation before leaving.
- Can inspect the current component.
- Can be reused across multiple forms.
- Supports synchronous and asynchronous checks.
Disadvantages / Considerations
- It only controls navigation performed through Angular Router.
- Browser refresh, tab closing, or closing the browser is a different browser lifecycle concern and may require additional handling.
- Too many confirmation dialogs can create poor UX.
- The application must correctly determine whether the form is actually dirty/unsaved.
For Angular forms, you can often use form state such as:
form.dirty
instead of manually maintaining:
hasUnsavedChanges
Key Points
CanDeactivatecontrols whether a user can leave a route.- Modern Angular uses:
CanDeactivateFn<T>
- Configure it using:
canDeactivate: [unsavedChangesGuard]
- Common use case:
Unsaved form changes
true→ allow leaving.false→ stay on the current route.- The guard receives the current component instance.
- It can work with
PromiseandObservable. - A reusable interface is useful when multiple components need the same unsaved-changes behavior.
Interview Questions and Answers
1. What is CanDeactivate?
CanDeactivate is an Angular route guard that determines whether a user can leave the currently activated route.
2. What is the most common use of CanDeactivate?
The most common use is preventing users from accidentally leaving a form with unsaved changes.
3. How do you configure CanDeactivate?
{
path: 'products/edit/:id',
component: EditProduct,
canDeactivate: [unsavedChangesGuard]
}
4. What happens when it returns true?
return true;
Angular allows the user to leave the current route.
5. What happens when it returns false?
return false;
Angular cancels the navigation, so the user remains on the current route.
6. What is the difference between CanActivate and CanDeactivate?
CanActivate
↓
Can the user ENTER the route?
CanDeactivate
↓
Can the user LEAVE the route?
CanActivate is commonly used for authentication/authorization, while CanDeactivate is commonly used for unsaved changes.
7. Why does CanDeactivateFn have a generic type?
For example:
CanDeactivateFn<EditProduct>
The generic type identifies the component being deactivated, allowing the guard to access that component instance:
component.canLeave()
8. Can CanDeactivate be asynchronous?
Yes. It can return asynchronous guard results using a Promise or Observable.
Interview-ready answer
CanDeactivateis an Angular route guard used to determine whether a user can leave the current route. It is commonly used to prevent users from accidentally leaving a form with unsaved changes. In modern Angular, it is implemented usingCanDeactivateFn<T>. Returningtrueallows navigation, while returningfalsecancels navigation and keeps the user on the current page.