Search code examples
angulartypescriptangular-routing

Angular - Block navigation from component (without route guard)


I'm trying to prevent navigation until the user confirms they will lose their changes, but I don't think I can use a route guard in my scenario.

Here is the check for changes:

public get hasChanged(): boolean {
    return this.changeManager.hasChanged({
        propA: this.propACtrl.value,
        propB: this.propBCtrl.value,
        // ETC, lots of other checks
    });
}
class ChangeManager {
    // bunch of properties containing original values

    public hasChanged(args: ChangeManagerArguments): boolean {
        return this.propA !== args.propA || this.propB !== args.propB // etc...
    }
}

Therefore, I don't think I can use a service, since this check is done at a specific timing, the service will not have that information (since it doesn't have access to all those properties it needs to check).

For the moment, I can handle the scenario where the user uses the browser features to leave the page (back button, home button, closing tab etc...) by doing this:

@HostListener("window:beforeunload")
public unloadHandler(): boolean {
    if (this.hasChanged) {
        return false;
    }

    return true;
}

How can I have something similar if the user navigates via a routerLink? Is there a way to achieve this with a route guard?


Solution

  • First need to create PendingChangesGuard as below:

    export interface ComponentCanDeactivate {
      canDeactivate: () => boolean | Observable<boolean>;
    }
    
    @Injectable()
    export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
      canDeactivate(component: ComponentCanDeactivate,
                route: ActivatedRouteSnapshot, 
                state: RouterStateSnapshot,
                nextState: RouterStateSnapshot): boolean | Observable<boolean> {
           
                 
        // if there are no pending changes, just allow deactivation; else confirm first
         return component.canDeactivate() ?
           true :
           confirm('WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
      }
    }
    

    Routing Path add Guard

    { path: 'path...', component: ComponentName, canDeactivate: [PendingChangesGuard] },
    

    //Write below code in component where you want to check the change done or not?

    @HostListener('window:beforeunload')
      canDeactivate(): Observable<boolean> | boolean {
        // insert logic to check if there are pending changes here;
        // returning true will navigate without confirmation
        // returning false will show a confirm dialog before navigating away
        if(this.list.length>0){   // Check whatever condition you have 
           return false;
        }
         else{
           return true;
        }
        
    }