2

So I have a SPA application built in angular, now my issue is when a user is on my app if they press the (browser) back button, sometimes if data was sent on the previous page it can cause errors and sometime there is state that when refreshed goes away. Now Is there a way I can warn a user before going back or simply not allow a user to go back??

I have tried to do this in my index.html

<script>
    window.onbeforeunload = function() {
       return "Message";
    };
</script>

but it looks like this no longer works on newer browsers, I found a similar question here but its from a few years ago and I've tried a few of there solutions and none of them worked..

what is the best way in 2018 to handle this situation??

Thanks

Smokey Dawson
  • 8,827
  • 19
  • 77
  • 152

2 Answers2

4

You can allow/prevent navigating to a route with a canActivate route guard, and you can allow/prevent navigating away from a route with a canDeactivate route guard (see the Angular documentation).

If you want to prevent a user from going back to a page that has already been visited, use a canActivate route guard. In this stackblitz, a confirmation is asked before going back to the Login page. As a bonus, it also contains an example of a canDeactivate route guard for the Home route.

Here is the code for the canActivate route guard of the Login route:

activate-guard.ts

import { Injectable } from "@angular/core";
import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";
import { Observable } from "rxjs";
import { LoginActivateService } from "./login-activate.service";

@Injectable()
export class ActivateGuard implements CanActivate {

  constructor(private loginActivate: LoginActivateService) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.loginActivate.canActivateLogin || confirm("Do you really want to go back to login?");
  }

}

app-routing.module.ts

...
imports: [
  RouterModule.forRoot([
    {
      path: 'login', component: LoginViewComponent,
      canActivate: [ActivateGuard]
    },
    {
      path: 'home',
      component: HomeViewComponent,
      canDeactivate: [DeactivateGuard]
    },
    ...
  ])
],
ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
  • will this trigger for every unload event or just the back button?? – Smokey Dawson Sep 10 '18 at 00:38
  • For any attempt to leave the page. I can make more tests to see if there is a way to tell that the back button was the cause. – ConnorsFan Sep 10 '18 at 00:39
  • Yeah I would really only like to display the message if a user presses the back button and not tries to leave the app – Smokey Dawson Sep 10 '18 at 00:42
  • Do you mean that you want to show the message when the user presses the back button to navigate in the application? This answer would not apply to that case (nor would the code sample in the question). – ConnorsFan Sep 10 '18 at 00:45
  • I mean the browser back button – Smokey Dawson Sep 10 '18 at 00:45
  • Yes, but that button can cause the browser to leave the application, or it can navigate to the previous route in the application. Which one applies to your case? – ConnorsFan Sep 10 '18 at 00:47
  • I basically just want to prevent people from using the back button to go previous pages in my application – Smokey Dawson Sep 10 '18 at 00:50
  • From that page, can the user navigate to other pages of the application by other means than the back button? – ConnorsFan Sep 10 '18 at 00:59
  • Yes I have full navigation build into the web app, there is no reason for a user to actually press the back button, but it was just brought up as an issue. – Smokey Dawson Sep 10 '18 at 01:00
  • Sorry, one more question. The user would be allowed to go to other pages, but you want to prevent (or warn) him from navigating to one particular page. Is that correct? – ConnorsFan Sep 10 '18 at 01:03
  • so the user can go to other places using the app navigation, but I want to prevent them or warn them from using the back button, not just for one particular page but for any page. – Smokey Dawson Sep 10 '18 at 01:04
  • 1
    You can use route guards to prevent navigation from a route (`canDeactivate`) or to a route (`canActivate`), but I don't think they can distinguish between using the back button and other navigation means. If, after leaving a page, you don't want the user to go back to that page (with or without the back button), it can be done with a `canActivate` route guard. – ConnorsFan Sep 10 '18 at 01:17
  • I modified the answer to show the usage of route guards. You can experiment with the stackblitz demo mentioned in the answer. – ConnorsFan Sep 10 '18 at 01:59
1

you can subscribe to the events property of the Router (available in Angular 8 >) it will trigger an action on each routing phase for example at navigation start, end , onPopstate event, ... and thus the browser back button event.

import { Router } from '@angular/router';

constructor(private router: Router) {
   this.router.events.subscribe((event) => {
  if (event instanceof NavigationStart && event.navigationTrigger === 'popstate') {
      // write the logic here
    }
   });
}