You need to do the following in the Angular way:
- Have a guard service that implements
CanDeactivate
(called when you navigate away from a page), and configure your router to use it
- Listen to router events in the page where you want the option to prevent the Back button from working
I also implemented a generic confirmation material dialog and service.
See the full example on Stackblitz here. Below is the important code snippets:
App Routes
const routes: Routes = [
/**
* About page is where we are guarding the navigation
*/
{ path: "about", component: AboutComponent, canDeactivate: [NavigationGuardService] },
{ path: "", component: HomeComponent }
];
Navigation Guard
import { Injectable } from "@angular/core";
import { CanDeactivate } from "@angular/router";
import { Observable } from "rxjs";
import { DataService } from "./data.service";
export interface DeactivationGuarded {
canDeactivate(): Observable<boolean> | Promise<boolean> | boolean;
}
@Injectable()
export class NavigationGuardService implements CanDeactivate<any> {
constructor(private dataService: DataService) {}
/**
* Angular calls it when navigating away from the backFromAboutPage
* Notice it is asynvhronous in our case because we are waiting for the user
* to confirm or deny
*/
canDeactivate(component: DeactivationGuarded): boolean | Observable<boolean> | Promise<boolean> {
if (!this.dataService.backFromAboutPage) {
return true;
}
return component.canDeactivate ? component.canDeactivate() : true;
}
}
The Monitored Page
import { Component, OnDestroy, OnInit } from "@angular/core";
import { MatDialogConfig } from "@angular/material/dialog";
import { NavigationStart, Router } from "@angular/router";
import { Observable, Subscription } from "rxjs";
import { filter } from "rxjs/operators";
import { ConfirmationDialogService } from "../confirmation-dialog.service";
import { DataService } from "../data.service";
import { DeactivationGuarded } from "../navigation-guard.service";
@Component({
selector: "app-about",
templateUrl: "./about.component.html",
styleUrls: ["./about.component.css"]
})
export class AboutComponent implements OnInit, OnDestroy, DeactivationGuarded {
routerSubscription: Subscription;
constructor(
private dataService: DataService,
private router: Router,
private confirmationDialogService: ConfirmationDialogService
) {}
ngOnInit(): void {
/**
* Listen to the router events to identify when the user navigates away from the page
* and distinguish between Back button and other means of navigation
*/
this.routerSubscription = this.router.events
.pipe(filter(event => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
if (event.navigationTrigger == "popstate" && event.restoredState) {
this.dataService.backFromAboutPage = true;
} else {
this.dataService.backFromAboutPage = false;
}
console.log("Back button:", this.dataService.backFromAboutPage);
});
}
ngOnDestroy(): void {
this.routerSubscription.unsubscribe();
}
/**
* Called from the navigation guard when navigating away from the page
* Here we show the confirmation dialog
*/
canDeactivate(): boolean | Observable<boolean> | Promise<boolean> {
const dialogConfig: MatDialogConfig = {
data: {
title: "",
content: `Go Back?`
}
};
this.confirmationDialogService.openDialog(dialogConfig);
return this.confirmationDialogService.back$.asObservable();
}
}