0

I'm working on an application with reactive forms where user fills up data and goes to next step by clicking Next button.
I've got a following problem to solve in my application: user can't navigate through website by manually changing url. If he/she tries, the expected outcome should be:

  1. Pop up informing that all data will be lost and user will be turned back to the first page.

  2. After user changes url and accepts the lost of data by clicking "Yes" user should start on the first page.

  3. The only time when the user should stay on the same page should be after refreshing the page.

I tried firstly do it in with guards, but then I couldn't find a way to tell Angular that the guard should only work if user changes url manually. The guard also worked while navigating using button Next - in other words the Next button didn't allow me to go to next step anymore.

Then I implemented something like this:

export class DirectAccessGuard implements CanActivate {

    constructor(private router: Router ) {
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

        if (this.router.url === '/') {
            this.router.navigate(['']);
            return false;
        }
        return true;
    }
}

and added this guard to each of my paths and it worked quite well until I realize this solution isn't perfect either because it redirects me to the first page after refreshing the page. Also tried CanDeactivate but I guess it isn't what I need here. Any ideas?

  • if all of the pages have only one URL, then the user cannot navigate between pages by changing the URL .... you could also save the user's progress at each step and return to the same spot – jsotola Mar 17 '20 at 19:28
  • Jerry, I have some doubt with point 3. If you are allowing manual refresh of that page, how are you maintaining the previous page data ? It seems like a `WORKFLOW` , so how are you allowing user to refresh the page and yet allow him to stay in middle of the WORKFLOW (in middle of chain of forms) ? – Shashank Vivek Mar 17 '20 at 19:43
  • It would be really easy for me to provide some solution if you can create a demo on stackblitz.com. Or else i'll wait for your reply – Shashank Vivek Mar 17 '20 at 19:44

1 Answers1

0

Angular guards can't prevent the browser from performing native navigation actions (refresh, manual URL change, close tab, etc). So generally, you'd want to use guards and in-app popups preventing Angular-based navigations like router.navigate() and [routerLink], but you'll need to fallback to native techniques to handle the native actions.

You can use beforeunload to handle that fallback. Note: as mentioned in this SO answer, Chromium won't allow you to input a custom message, but you can go through the steps for other browsers.

Here's a Stackblitz showing it in action, but here's the gist. You can create an Observable out of the native window.beforeunload event, filter results so it only fires when it should (e.g. this.warn would change to whatever boolean value or expression you want to check), then subscribe to it and set your return values as below:

fromEvent(window, 'beforeunload')
  .pipe(
    filter(() => this.warn)
  )
  .subscribe((event: BeforeUnloadEvent) => {
    const message = 'You may lose your data if you refresh now';
    (event || window.event).returnValue = message;
    return message;
  });
Scrimothy
  • 2,536
  • 14
  • 23