Angular CanDeactivate Guard Example

By Arvind Rai, August 29, 2021
This page will walk through Angular CanDeactivate guard example. The CanDeactivate is an interface that is implemented by our class to create a route guard to decide if a route can be deactivated. The guard can be added to any component route using canDeactivate attribute of Angular Route interface. Any route which needs to use CanDeactivate guard, has to define canDeactivate method and that will be called by our route guard service. We can also create component-specific CanDeactivate guard. The route guard is created as Angular service and Router can inject it during the navigation process. While deactivating route using CanDeactivate guard, we need to open a confirmation dialog box to take user confirmation if user wants to stay on the page or leave the page.
The CanDeactivate guard can be used in the scenario where a user is changing form data and before saving, user tries to navigate away. In this scenario we can use CanDeactivate guard to deactivate the route and open a Dialog Box to take user confirmation.
Here on this page we will create CanDeactivate guard that can be used with any component and we will also create component specific CanDeactivate guard. Now let us discuss complete example step-by-step.

Technologies Used

Find the technologies being used in our example.
1. Angular 12.1.0
2. Node.js 12.14.1
3. NPM 7.20.3

2. CanDeactivate Interface

CanDeactivate interface is a route guard to decide if a route can deactivated. Find the structure of CanDeactivate interface from Angular doc.
 interface CanDeactivate<T> { 
    canDeactivate(component: T, 
                  currentRoute: ActivatedRouteSnapshot,
                  currentState: RouterStateSnapshot,
                  nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean
} 
CanDeactivate interface has a method canDeactivate that accepts arguments as given below.
a. Component for which route is to be deactivated.
b. ActivatedRouteSnapshot
c. RouterStateSnapshot
d. RouterStateSnapshot

canDeactivate method will return Observable<boolean> or Promise<boolean> or boolean .

3. Steps to Create CanDeactivate Guard for any Component

To use CanDeactivate in our application, we need to create a guard. This will be a service decorated with @Injectable() and will define canDeactivate() method. If a component needs CanDeactivate route guard then that component has also to create a method named as canDeactivate() .
Find the sample output of our application for CanDeactivate guard.
Angular CanDeactivate Guard Example
Now we will discuss step-by-step to use CanDeactivate in our application.

3.1 Create Service for CanDeactivate Guard

First we will create an interface that will declare canDeactivate method and using this interface we will create a service that will act as CanDeactivate guard. This service will define canDeactivate method. Now find our CanDeactivate guard.
can-deactivate-guard.service.ts
export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate, 
           route: ActivatedRouteSnapshot, 
           state: RouterStateSnapshot) {

     let url: string = state.url;
     console.log('Url: '+ url);

     return component.canDeactivate ? component.canDeactivate() : true;
  }
} 
The interface has declared canDeactivate method whose return type is Observable<boolean> or Promise<boolean> or boolean . In the service code, we need to call canDeactivate method using component instance. For any component which has defined canDeactivate method, that method will be called by component instnace otherwise true will be returned by the above guard. We can access current route and current state, too. In this way we have created our CanDeactivate guard .i.e. CanDeactivateGuard service in our example that can be used for any component.

3.2 Configure CanDeactivate Guard Service in Application Routing Module

We will configure our CanDeactivate guard service i.e. CanDeactivateGuard in application routing module using providers attribute of @NgModule decorator. We need to add it in main application routing module so that Router can inject it during the navigation process.
import { CanDeactivateGuard } from './can-deactivate-guard.service';

------
@NgModule({
  ------
  providers: [ 
    CanDeactivateGuard
  ]
})
export class AppRoutingModule { } 

3.3 Create Service for Confirmation Dialog Box

Create a service for Confirmation Dialog Box.
dialog.service.ts
@Injectable()
export class DialogService {
  confirm(message?: string): Observable<boolean> {
    const confirmation = window.confirm(message || 'Are you sure?');

    return Observable.of(confirmation);
  };
} 
We are passing an optional message for Dialog Box. The Boolean result will be returned as Observable. We need to configure the above service in application module using providers attribute of @NgModule decorator.

3.4 Create canDeactivate() method within Component

We have created CanDeactivate guard .i.e. CanDeactivateGuard and it can be used for any component. Suppose we have a component as PersonEditComponent that will perform update operation for person details. If we want to use CanDeactivate guard for this component then we need to create canDeactivate() method within this component.
@Component({
  templateUrl: './person.edit.component.html' 
}) 
export class PersonEditComponent implements OnInit { 
        ------
	constructor( public dialogService: DialogService) { }

	canDeactivate(): Observable<boolean> | boolean {

	    if (!this.isUpdating && this.personForm.dirty) {

        	return this.dialogService.confirm('Discard changes for Person?');
	    }
	    return true;
	}	
} 
The method canDeactivate() of this component will be called by CanDeactivateGuard service. The canDeactivate() method of the above component will open a Dialog Box to ask if we want to stay on the page or leave the page.

3.5 Add CanDeactivate Guard to Component Route in Routing Module

We need to add our CanDeactivate guard .i.e. CanDeactivateGuard to component route in routing module using canDeactivate attribute.
const personRoutes: Routes = [
  { 
    path: 'person',
    component: PersonComponent,
    children: [ 
      {
         path: '',
         component: PersonListComponent,
         children: [
           {
             path: ':id',
             component: PersonEditComponent,
     	     canDeactivate: [CanDeactivateGuard]
           }
         ]			
       }
     ]
  }  
];

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class PersonRoutingModule{ } 
In this way, we can use CanDeactivate guard in our application for any component.

4. Component-Specific CanDeactivate Guard

We can create component specific CanDeactivate guard. Suppose we have a component CountryEditComponent and we want to create CanDeactivate guard for this component. The guard needs to implement Angular CanDeactivate interface and will define canDeactivate() method. Find the code as following.
country-edit-can-deactivate-guard.service.ts
@Injectable()
export class CountryEditCanDeactivateGuard implements CanDeactivate<CountryEditComponent> {
  
  constructor(private dialogService: DialogService) { }

  canDeactivate(
    component: CountryEditComponent,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {
    
      let url: string = state.url;
      console.log('Url: '+ url);

      if (!component.isUpdating && component.countryForm.dirty) {
        component.isUpdating = false;
        return this.dialogService.confirm('Discard changes for Country?');
      }
      return true;
  }
} 
Within canDeactivate() method we need to open our Confirmation Dialog Box.

We will configure CountryEditCanDeactivateGuard in main application routing module so that Router can inject it during the navigation process.
import { CanDeactivateGuard } from './can-deactivate-guard.service';
import { CountryEditCanDeactivateGuard } from './country-edit-can-deactivate-guard.service';

------
@NgModule({
  ------
  providers: [ 
    CanDeactivateGuard,
    CountryEditCanDeactivateGuard
  ]
})
export class AppRoutingModule { } 
Now add guard to CountryEditComponent route in routing module using canDeactivate attribute.
const countryRoutes: Routes = [
  { 
      path: 'country',
      component: CountryComponent,
      children: [ 
	{
	   path: 'add',
	   component: AddCountryComponent,
	   canDeactivate: [CanDeactivateGuard]
	},
	{
	   path: 'list',
	   component: CountryListComponent,
	   children: [
	     {
	        path: 'edit/:country-id',
		component: CountryEditComponent,
		canDeactivate: [CountryEditCanDeactivateGuard]
	     }	   
	   ]
	 }	
       ]
    }  
];

@NgModule({
  imports: [ RouterModule.forChild(countryRoutes) ],
  exports: [ RouterModule ]
})
export class CountryRoutingModule { } 
In this way, we can use CanDeactivate guard in our application for a specific component.

5. Complete Example

Find the project structure of our application.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--can-deactivate-guard.service.ts
|   |   |--country-edit-can-deactivate-guard.service.ts
|   |   |--dialog.service.ts
|   |   |
|   |   |--country
|   |   |    | 
|   |   |    |--country.component.ts
|   |   |    |--country.ts
|   |   |    |--country.module.ts
|   |   |    |--country-routing.module.ts
|   |   |    |
|   |   |    |--add-country
|   |   |    |    |  
|   |   |    |    |--add-country.component.html
|   |   |    |    |--add-country.component.ts
|   |   |    |    
|   |   |    |--country-list
|   |   |    |    |
|   |   |    |    |--country.list.component.html
|   |   |    |    |--country.list.component.ts
|   |   |    |    |
|   |   |    |    |--edit
|   |   |    |    |   |
|   |   |    |    |   |--country.edit.component.html
|   |   |    |    |   |--country.edit.component.ts
|   |   |    |    |
|   |   |    |    
|   |   |    |--services
|   |   |    |    |
|   |   |    |    |--country.service.ts
|   |   |    | 
|   |   |
|   |   |--person
|   |   |    | 
|   |   |    |--person.component.ts
|   |   |    |--person.ts
|   |   |    |--person-routing.module.ts
|   |   |    |--person.module.ts
|   |   |    | 
|   |   |    |--person-list 
|   |   |    |    |
|   |   |    |    |--person.list.component.html 
|   |   |    |    |--person.list.component.ts
|   |   |    |    |
|   |   |    |    |--edit
|   |   |    |    |   |
|   |   |    |    |   |--person.edit.component.html
|   |   |    |    |   |--person.edit.component.ts
|   |   |    | 
|   |   |    |--services
|   |   |    |    |  
|   |   |    |    |--person.service.ts
|   |   |    |
|   |   |
|   |   |--page-not-found.component.ts
|   |   |--app.component.ts
|   |   |--app-routing.module.ts
|   |   |--app.module.ts
|   |   
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json
Now find the complete code.
can-deactivate-guard.service.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

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

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot) {

    let url: string = state.url;
    console.log('Url: ' + url);

    return component.canDeactivate ? component.canDeactivate() : true;
  }
} 
country-edit-can-deactivate-guard.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { CountryEditComponent } from './country/country-list/edit/country.edit.component';
import { DialogService } from './dialog.service';

@Injectable()
export class CountryEditCanDeactivateGuard implements CanDeactivate<CountryEditComponent> {

  constructor(private dialogService: DialogService) { }

  canDeactivate(
    component: CountryEditComponent,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {

    let url: string = state.url;
    console.log('Url: ' + url);

    if (!component.isUpdating && component.countryForm.dirty) {
      component.isUpdating = false;
      return this.dialogService.confirm('Discard changes for Country?');
    }
    return true;
  }
} 
dialog.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable()
export class DialogService {
  confirm(message?: string): Observable<boolean> {
    const confirmation = window.confirm(message || 'Are you sure?');

    return of(confirmation);
  };
} 
country.component.ts
import { Component } from '@angular/core';

@Component({
  template: `<h2>Welcome to Country Home</h2>
      <nav [ngClass] = "'child-menu'">
        <ul>
	    <li><a [routerLink]="['add']" routerLinkActive="active">Add Country</a></li>
	    <li><a [routerLink]="['list']" routerLinkActive="active">Country List</a></li>
        </ul>  
      </nav>  
      <div [ngClass] = "'child-container'">	
	   <router-outlet></router-outlet>	
      </div>
  `
})
export class CountryComponent { 
} 
country.ts
export class Country { 
	constructor(public countryId:number, public name:string,
            	public capital:string, public currency:string) {
	}
} 
country.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { ReactiveFormsModule }    from '@angular/forms';
import { CountryComponent }  from './country.component';
import { AddCountryComponent }  from './add-country/add-country.component';
import { CountryListComponent }  from './country-list/country.list.component';
import { CountryEditComponent }  from './country-list/edit/country.edit.component';
import { CountryService } from './services/country.service';
import { CountryRoutingModule }  from './country-routing.module';

@NgModule({
  imports: [     
         CommonModule,
	 ReactiveFormsModule,
	 CountryRoutingModule
  ], 
  declarations: [
	 CountryComponent,
	 AddCountryComponent,
	 CountryListComponent,
	 CountryEditComponent
  ],
  providers: [ CountryService ]
})
export class CountryModule { } 
country-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CountryComponent }  from './country.component';
import { CountryListComponent }  from './country-list/country.list.component';
import { AddCountryComponent }  from './add-country/add-country.component';
import { CountryEditComponent }  from './country-list/edit/country.edit.component';
import { CanDeactivateGuard }     from '../can-deactivate-guard.service';
import { CountryEditCanDeactivateGuard }     from '../country-edit-can-deactivate-guard.service';

const countryRoutes: Routes = [
   { 
      path: 'country',
      component: CountryComponent,
      children: [ 
	 {
	     path: 'add',
	     component: AddCountryComponent,
	     canDeactivate: [CanDeactivateGuard]
	 },
	 {
	     path: 'list',
	     component: CountryListComponent,
	     children: [
	        {
		   path: 'edit/:country-id',
		   component: CountryEditComponent,
		   canDeactivate: [CountryEditCanDeactivateGuard]
		}	   
	     ]
	 }	
       ]
    }  
];

@NgModule({
  imports: [ RouterModule.forChild(countryRoutes) ],
  exports: [ RouterModule ]
})
export class CountryRoutingModule { } 
add-country.component.html
<h3>Add Country</h3>
<form [formGroup]="countryForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> Capital: <input formControlName="capital"> </p>
   <p> Currency: <input formControlName="currency"> </p>
   <p> <button>Add</button> </p>
</form> 
add-country.component.ts
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { CountryService } from '../services/country.service';
import { Country } from '../country';
import { DialogService } from '../../dialog.service';
import { Observable } from 'rxjs';

@Component({
	templateUrl: './add-country.component.html'
})
export class AddCountryComponent {
	isAdding = false;
	constructor(
		private countryService: CountryService,
		private route: ActivatedRoute,
		private router: Router,
		private formBuilder: FormBuilder,
		private dialogService: DialogService) { }

	countryForm = this.formBuilder.group({
		name: '',
		capital: '',
		currency: ''
	});
	onFormSubmit() {
		this.isAdding = true;
		let name = this.countryForm.get('name')?.value;
		let capital = this.countryForm.get('capital')?.value;
		let currency = this.countryForm.get('currency')?.value;

		let country = new Country(0, name, capital, currency);
		this.countryService.addCountry(country)
			.subscribe(() =>
				this.router.navigate(['../list'], { relativeTo: this.route })
			);
	}
	canDeactivate(): Observable<boolean> | boolean {
		if (!this.isAdding && this.countryForm.dirty) {
			return this.dialogService.confirm('Discard unsaved Country?');
		}
		return true;
	}
} 
country.list.component.html
<h3>Country List</h3>
<div *ngFor="let country of countries | async" [ngClass]= "'sub-child-menu'">
 <p> {{country.countryId}} - {{country.name}} -
     {{country.capital}} - {{country.currency}}
     | <a [routerLink]="['edit', country.countryId]" routerLinkActive="active">Edit</a>
 </p>
</div>
<div [ngClass]= "'sub-child-container'">
   <router-outlet></router-outlet>  
</div> 
country.list.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { CountryService } from '../services/country.service';
import { Country } from '../country';

@Component({
  templateUrl: './country.list.component.html'
})
export class CountryListComponent implements OnInit {
  countries: Observable<Country[]>;
  constructor(private countryService: CountryService) {
    this.countries = this.countryService.getCountries();
  }
  ngOnInit() {
  }
} 
country.edit.component.html
<h3>Edit Country</h3>
<p *ngIf="country"><b>Country Id: {{country.countryId }} </b></p>
<form [formGroup]="countryForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> Capital: <input formControlName="capital"> </p>
   <p> Currency: <input formControlName="currency"> </p>
   <p> <button>Update</button> </p>
</form>  
country.edit.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { FormGroup, FormBuilder } from '@angular/forms';
import { switchMap } from 'rxjs/operators';
import { CountryService } from '../../services/country.service';
import { Country } from '../../country';

@Component({
	templateUrl: './country.edit.component.html'
})
export class CountryEditComponent implements OnInit {
	country = {} as Country;
	countryForm = {} as FormGroup;
	isUpdating = false;
	constructor(
		private countryService: CountryService,
		private route: ActivatedRoute,
		private router: Router,
		private formBuilder: FormBuilder) { }

	ngOnInit() {
		this.route.params.pipe(
			switchMap((params: Params) => this.countryService.getCountry(+params['country-id']))
		).subscribe(country => {
			this.country = country ?? {} as Country;
			this.createForm(country);
		});
	}
	createForm(country: Country | undefined) {
		this.countryForm = this.formBuilder.group({
			name: country?.name,
			capital: country?.capital,
			currency: country?.currency
		});
	}
	onFormSubmit() {
		this.isUpdating = true;
		this.country.name = this.countryForm.get('name')?.value;
		this.country.capital = this.countryForm.get('capital')?.value;
		this.country.currency = this.countryForm.get('currency')?.value;

		this.countryService.updateCountry(this.country)
			.subscribe(() =>
				this.router.navigate(['../../'], { relativeTo: this.route })
			);
	}
} 
country.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { Country } from '../country';

const COUNTRIES = [
	new Country(1, 'India', 'New Delhi', 'INR'),
	new Country(2, 'China', 'Beijing', 'RMB')
];
let countriesObservable = of(COUNTRIES);

@Injectable()
export class CountryService {
	getCountries() {
		return countriesObservable;
	}
	getCountry(id: number) {
		return this.getCountries().pipe(
			map(countries => countries.find(country => country.countryId === id))
		);
	}
	updateCountry(country: Country) {
		return this.getCountries().pipe(
			map(countries => {
				let countryObj = countries.find(ob => ob.countryId === country.countryId);
				countryObj = country;
				return countryObj;
			}));
	}
	addCountry(country: Country) {
		return this.getCountries().pipe(
			map(countries => {
				let maxIndex = countries.length - 1;
				let countryWithMaxIndex = countries[maxIndex];
				country.countryId = countryWithMaxIndex.countryId + 1;
				countries.push(country);
				return country;
			}));
	}
} 
person.component.ts
import { Component } from '@angular/core';

@Component({
  template: `
        <h2>Welcome to Person Home</h2>
	<div [ngClass] = "'child-container'">	
	  <router-outlet></router-outlet>	
	</div>
  `
})
export class PersonComponent { 
} 
person.ts
export class Person { 
	constructor(public personId:number, public name:string, public city:string) {
	}
} 
person-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PersonComponent }  from './person.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { PersonEditComponent }  from './person-list/edit/person.edit.component';
import { CanDeactivateGuard } from '../can-deactivate-guard.service';

const personRoutes: Routes = [
	{ 
	  path: 'person',
          component: PersonComponent,
	  children: [ 
	    {
	       path: '',
	       component: PersonListComponent,
	       children: [
	         {
	           path: ':id',
	     	   component: PersonEditComponent,
		   canDeactivate: [CanDeactivateGuard]
		 }
	       ]			
	     }
	  ]
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class PersonRoutingModule{ } 
person.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { ReactiveFormsModule }    from '@angular/forms';
import { PersonComponent }  from './person.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { PersonEditComponent }  from './person-list/edit/person.edit.component';
import { PersonService } from './services/person.service';
import { PersonRoutingModule }  from './person-routing.module';

@NgModule({
  imports: [     
        CommonModule,
	ReactiveFormsModule,
	PersonRoutingModule
  ], 
  declarations: [
	PersonComponent,
	PersonListComponent,
	PersonEditComponent
  ],
  providers: [ PersonService ]
})
export class PersonModule { } 
person.list.component.html
<h3>Person List</h3>
<div *ngFor="let person of persons | async" [ngClass]= "'sub-child-menu'">
  <p>{{person.personId}}. {{person.name}}, {{person.city}}
      <button type="button" (click)="goToEdit(person)">Edit</button>
  </p>
</div>
<div [ngClass]= "'sub-child-container'">
   <router-outlet></router-outlet>  
</div>  
person.list.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { PersonService } from '../services/person.service';
import { Person } from '../person';

@Component({
  templateUrl: './person.list.component.html'
})
export class PersonListComponent implements OnInit {
  persons: Observable<Person[]>;
  constructor(
    private personService: PersonService,
    private route: ActivatedRoute,
    private router: Router) {
    this.persons = this.personService.getPersons();
  }
  ngOnInit() {
  }
  goToEdit(person: Person) {
    this.router.navigate([person.personId], { relativeTo: this.route });
  }
} 
person.edit.component.html
<h3>Edit Person</h3>
<p *ngIf="person"><b>Person Id: {{person.personId }} </b></p>
<form [formGroup]="personForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> City: <input formControlName="city"> </p>
   <p> <button>Update</button> </p>
</form> 
person.edit.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { FormGroup, FormBuilder } from '@angular/forms';
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { PersonService } from '../../services/person.service';
import { Person } from '../../person';
import { DialogService } from '../../../dialog.service';

@Component({
	templateUrl: './person.edit.component.html'
})
export class PersonEditComponent implements OnInit {
	person = {} as Person;
	personForm = {} as FormGroup;
	isUpdating = false;
	constructor(
		private personService: PersonService,
		private route: ActivatedRoute,
		private router: Router,
		private formBuilder: FormBuilder,
		private dialogService: DialogService) { }

	ngOnInit() {
		this.route.params.pipe(
			switchMap((params: Params) => this.personService.getPerson(+params['id']))
		).subscribe(person => {
			this.person = person ?? {} as Person;
			this.createForm(person);
		});
	}
	createForm(person: Person | undefined) {
		this.personForm = this.formBuilder.group({
			name: person?.name,
			city: person?.city
		});
	}
	onFormSubmit() {
		this.isUpdating = true;
		this.person.name = this.personForm.get('name')?.value;
		this.person.city = this.personForm.get('city')?.value;
		this.personService.updatePerson(this.person)
			.subscribe(() =>
				this.router.navigate(['../'], { relativeTo: this.route })
			);
	}
	canDeactivate(): Observable<boolean> | boolean {
		if (!this.isUpdating && this.personForm.dirty) {
			this.isUpdating = false;
			return this.dialogService.confirm('Discard changes for Person?');
		}
		return true;
	}
} 
person.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { Person } from '../person';

const PERSONS = [
  new Person(1, 'Mahesh', 'Varanasi'),
  new Person(2, 'Ram', 'Ayodhya'),
  new Person(3, 'Krishn', 'Mathura')
];
let personsObservable = of(PERSONS);

@Injectable()
export class PersonService {
  getPersons(): Observable<Person[]> {
    return personsObservable;
  }
  getPerson(id: number) {
    return this.getPersons().pipe(
      map(persons => persons.find(person => person.personId === id))
    );
  }
  updatePerson(person: Person) {
    return this.getPersons().pipe(
      map(persons => {
        let personObj = persons.find(ob => ob.personId === person.personId);
        personObj = person;
        return personObj;
      }));
  }
} 
page-not-found.component.ts
import { Component } from '@angular/core';
import { Location } from '@angular/common';

@Component({
  template: `<h2>Page Not Found.</h2>
             <div>
                <button (click)="goBack()">Go Back</button>
	     </div>
            `
})
export class PageNotFoundComponent {
	constructor(private location: Location) { }
	goBack(): void {
           this.location.back();
        }
} 
app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
		<nav [ngClass] = "'parent-menu'">
		   <ul>
		     <li><a routerLink="/country" routerLinkActive="active">Country</a></li>
		     <li><a routerLink="/person" routerLinkActive="active">Person</a></li>
		   </ul> 
		</nav>  
		
		<router-outlet></router-outlet>	
  `
})
export class AppComponent { 
} 
app-routing.module.ts
import { NgModule }      from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PageNotFoundComponent }  from './page-not-found.component';
import { CanDeactivateGuard } from './can-deactivate-guard.service';
import { CountryEditCanDeactivateGuard } from './country-edit-can-deactivate-guard.service';

const routes: Routes = [
	{
	   path: '',
	   redirectTo: '/country',
	   pathMatch: 'full'
	},
	{
	   path: '**',
	   component: PageNotFoundComponent 
	}
];

@NgModule({
  imports: [ 
      RouterModule.forRoot(routes) 
  ],
  exports: [ 
      RouterModule 
  ],
  providers: [ 
      CanDeactivateGuard,
      CountryEditCanDeactivateGuard,
  ],
})
export class AppRoutingModule { }  
app.module.ts
import { NgModule }   from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { PageNotFoundComponent }  from './page-not-found.component';
import { CountryModule }  from './country/country.module';
import { PersonModule }  from './person/person.module';
import { AppRoutingModule }  from './app-routing.module';
import { DialogService } from './dialog.service';

@NgModule({
  imports: [     
    BrowserModule,
    CountryModule,
    PersonModule,
    AppRoutingModule,
  ],
  declarations: [
    AppComponent,
    PageNotFoundComponent
  ],
  providers: [ 
    DialogService 
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { } 
styles.css
.parent-menu ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #333;
}
.parent-menu li {
    float: left;
}
.parent-menu li a {
    display: block;
    color: white;
    text-align: center;
    padding: 15px 15px;
    text-decoration: none;
}
.parent-menu li a:hover:not(.active) {
    background-color: #111;
}
.parent-menu .active{
    background-color: #4CAF50;
}
.child-container {
    padding-left: 10px;
}
.sub-child-container {
    padding-left: 10px;
}
.child-menu  {
    padding-left: 25px;
}
.child-menu .active{
    color: #4CAF50;
}
.sub-child-menu {
    background-color: #f1f1f1;  
    width: 275px;
    list-style-type: none;	
    margin: 0;
    padding: 0;
}
.sub-child-menu .active{
    color: #4CAF50;
}
button {
    background-color: #008CBA;
    color: white;
} 

6. Run Application

To run the application, find following steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Now access the URL http://localhost:4200
Click on the country and go to add country. Fill data and try to navigate away. We will get Confirmation Dialog Box to ask if we want to discard unsaved data.
Angular CanDeactivate Guard Example

7. References

Angular Doc: CanDeactivate
Common Routing Tasks
Angular Child Routes and Relative Navigation Example

8. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI









©2023 concretepage.com | Privacy Policy | Contact Us