What is CanDeactivate Angular?
CanDeactivate is a TypeScript interface that needs to be partially implemented in order to create a router. This guard will be used by the route to determine if the route will be closed. Can be applied to any Angular component using the canDeactivate interface.
Use case
You can not leave the current page to any route path from any where without save the form if you have unsaved changes data in form on the current page.
At first we will create a form in a page and that page route guard by CanDeactivate method. On same page implements a CanDeactivate method that will give you prompt warning for any change in form to save.
Follow the below step to complete canDeactivate use case.
Step:1
Create a canDeactivate guard where CanDeactivate angular interface router method will be implement which return a Boolean value from your component. Next ComponentCanDeactivate interface will implement in your guard component.
import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
export interface ComponentCanDeactivate {
canDeactivate: () => boolean | Observable;
}
@Injectable()
export class unSaveChangeGuard
implements CanDeactivate
{
constructor() {}
canDeactivate(
component: ComponentCanDeactivate
): boolean | Observable {
return component.canDeactivate();
}
}
Step: 2
Implements list of points below
- Implements ComponentCanDeactivate interface
- Create a reactive form and that form subscribe a subject to track valueChanges of the registration form
- Use primng dialog service alert to prompt you have a value change.
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Observable, Observer, of, Subject, takeUntil } from 'rxjs';
import { ComponentCanDeactivate } from './guard/un-save-change.guard';
import { HttpService } from './service/http.service';
import { ConfirmationService } from 'primeng/api';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'hello',
template: `
CanDeactivate Example
`,
styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements OnInit, ComponentCanDeactivate, OnDestroy {
registerForm!: FormGroup;
submitted = false;
private hasUnsavedData = false;
private unsubscribe = new Subject();
constructor(
private formBuilder: FormBuilder,
private _confirmationService: ConfirmationService,
private _httpService: HttpService
) {
this.registerForm = this.formBuilder.group({
firstName: new FormControl('', [Validators.required])
});
}
ngOnInit() {
this.isFormValueChanges();
}
get formcontrol() {
return this.registerForm.controls;
}
isFormValueChanges() {
const form = this.registerForm;
const initialValue = form.value;
form.valueChanges.pipe(takeUntil(this.unsubscribe)).subscribe((value) => {
this.hasUnsavedData = Object.keys(initialValue).some(
(key) => form.value[key] != initialValue[key]
);
});
}
onSubmit() {}
canDeactivate(): Observable | boolean {
if (this.hasUnsavedData) {
return new Observable((observer: Observer) => {
this._confirmationService.confirm({
message:
'You have unsaved changes. Do you want to save and leave this page? ',
accept: () => {
this.isChanges().subscribe({
next: (apiResult) => {
observer.next(true);
observer.complete();
},
error: (apiError) => {
observer.next(false);
observer.complete();
},
});
},
reject: () => {
observer.next(false);
observer.complete();
},
});
});
} else {
return of(true);
}
}
isChanges() {
return new Observable((observer: Observer) => {
this._httpService.fakeApiData().subscribe({
next: (apiResult) => {
console.log('fdd',apiResult)
observer.next(true);
observer.complete();
},
error: (apiError) => {
observer.next(false);
observer.complete();
},
});
});
}
ngOnDestroy() {
this.unsubscribe.next();
}
}
Step: 3
- Create a new Component name Welcome Component that link from Hello Component to test Route guard after change value in form.
@Component({
selector: 'hello',
template: `
CanDeactivate Example
Go to WelcomeComponent`,
styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements OnInit, ComponentCanDeactivate, OnDestroy {
registerForm!: FormGroup;
Step: 4
Final step add routing path and guard in RouterModule
const routes: Routes = [
{
path: '',
redirectTo: 'hello',
pathMatch: 'full'
},
{
path: 'hello',
component: HelloComponent,
canDeactivate: [unSaveChangeGuard]
},{
path: 'welcome',
component: WelcomeComponent
}
];
@NgModule({
imports: [ BrowserModule, FormsModule, ReactiveFormsModule,RouterModule.forRoot(routes),HttpClientModule,ConfirmDialogModule,BrowserAnimationsModule],
providers: [unSaveChangeGuard,HttpService,ConfirmationService],
declarations: [ AppComponent, HelloComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Stackblitz Live example
References
Thank you for read this article! Like our Facebook page to get more latest interesting articles update.