If I have a form in a view (Angular). When user tries to navigate away from there, and I want a confirmation message to appear. How can I do that?
You can implement canDeactivate using typescript like
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { ViewthatyouwantGuard } from './path to your view';
@Injectable()
export class ConfirmDeactivateGuard implements CanDeactivate<ViewthatyouwantGuard> {
canDeactivate(target: ViewthatyouwantGuard) {
if (target.hasChanges) {
return window.confirm('Do you really want to cancel?');
}
return true;
}
}
// hasChanges - function in 'ViewthatyouwantGuard' which will return true or false based on unsaved changes
// And in your routing file provide root like
{path:'rootPath/', component: ViewthatyouwantGuard, canDeactivate:[ConfirmDeactivateGuard]},
// Last but not least, also this guard needs to be registered accordingly:
@NgModule({
...
providers: [
...
ConfirmDeactivateGuard
]
})
export class AppModule {}
Source: https://blog.thoughtram.io/angular/2016/07/18/guards-in-angular-2.html
In My example site the user should verify secure pin while navigating to his/her Personal Details page. For all other pages user don't need secure pin. After Secure pin verification success then only he should navigate to "Personal Details" otherwise the user should be in same page.
private routerChangeListener() {
this.router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.isPersonalDetailsNavigation(event.url);
}
});
}
private verfiyIsNavigatingToMedication(url: string) {
url = url.replace('/', '');
if (url === 'personal-details') {
showPinVerficationPopup();
} else {
console.log('This Is Not Personal Details Page Navigation');
}
}
private showPinVerficationPopup() {
if(verificationSuccess) {
// This Will Navigate to Personal Details Page
this.router.navigate(['personal-details']);
} else {
// This Will Prevent Navigation
this.router.navigate([this.router.url]);
}
}
If your site needs prevent route change for multiple pages, i suppose it might be convenient to use this service:
import { Injectable } from '@angular/core';
import { CanActivateChild } from '@angular/router';
@Injectable()
export class NavPreventerService implements CanActivateChild {
locks: any = {};
constructor() {
// Bonus: If necessary also prevent closing the window:
window.addEventListener('beforeunload', (event) => {
if (this.hasRoutingLocks()) {
event.preventDefault();
event.returnValue = '';
}
});
}
canActivateChild() {
if (this.hasRoutingLocks()) {
if (confirm('Leave site?')) {
this.removeAllRoutingLocks();
return true;
}
return false;
}
return true;
}
setRoutingLock(key: string) {
this.locks[key] = true;
}
removeRoutingLock(key: string) {
delete this.locks[key];
}
removeAllRoutingLocks() {
this.locks = {};
}
hasRoutingLocks(): boolean {
return !!Object.keys(this.locks).length;
}
}
When it is necessary to prevent navigation, then in your component call
this.navPreventer.setRoutingLock('quiz-form');
Your application routing file should be like this:
export const appRoutesLocal: Routes = [
{path: '', canActivateChild: [NavPreventerService], children: [
{path: '', component: HomePageComponent},
]}
];
CanDeactivate can be used for that. You need to pass the status to a service that is accessible to canDeactivate
.