Angular 6: simple confirmation on page leaving

hofshteyn picture hofshteyn · Jan 15, 2019 · Viewed 11.1k times · Source

I need to make a simple confirm window and I saw a lot of examples of how to do it with extra actions (like waiting until file uploading of form is not field). But I need just make a default confirm window with a default text (like in a pic below) to show it when the user wants to leave from the current page. And I can't completely understand what logic should I proved inside handling before unload event.

image example

I'm recently sorry if it a dup of some question, however, I didn't find any solution. So I have:

example.guard.ts

export interface CanComponentDeactivate {
    canDeactivate: () => Observable<boolean> | boolean;
}

@Injectable()
export class ExampleGuard implements CanDeactivate<CanComponentDeactivate> {

    constructor() { }

    canDeactivate(component: CanComponentDeactivate): boolean | Observable<boolean> {
        return component.canDeactivate() ?
            true :
            confirm('message'); // <<< does confirm window should appear from here?
    }
}

example.component.ts

export class ExampleComponent implements CanComponentDeactivate {

    counstructor() { }

    @HostListener('window:beforeunload', ['$event'])
        canDeactivate($event: any): Observable<boolean> | boolean {
            if (!this.canDeactivate($event)) {
                // what should I do here?
            }
        }
}

It would be wonderful if you provide a sample of code but I appreciate any kind of help.

Answer

Danil Gudz picture Danil Gudz · Jan 15, 2019

You should differentiate beforeunload native event on window and canDeactivate guard. First one is being triggered when you are trying to close tab/window. So that when it is triggered you can confirm(...) user and execute event.preventDefault() on it to cancel closing tab/window.

Talking about CanDeactivate guard it should return an observable/promise/plain-value of boolean which will tell you if you can deactivate current route.

So it is much better to separate two methods (one for beforeunload and second one for the guard). Because if you want then change behavior to not just use native confirm but your custom modal window the default event handler for beforeunload won't work as it handles sync code. So for beforeunload you can use confirm only to ask user not to leave the page.

loading = true;
@HostListener('window:beforeunload', ['$event'])
canLeavePage($event: any): Observable<void> {
  if(this.loading && confirm('You data is loading. Are you sure you want to leave?')) {
    $event.preventDefault();
  }
}

Guard on the other hand wants boolean to be returned (or Promise, or Observable). So here you can just return the result of your condition:

canDeactivate(): boolean {
  return this.loading && confirm('You data is loading. Are you sure you want to leave?');
}

So that in your CanDeactivate guard it will be used like return component.canDeactivate()