1

I have multiple forms in an app and I have a CanDeactivate guard that prompts the user to confirm whether they want to leave the page without first saving the forms they have edited. Each component with forms has a hasBeenEdited function that checks if the form has been edited. Since I have only one CanDeactivate injectable class to handle all these components with forms, I need to access the hasBeenEdited function of the component where the user currently routed to. How best to accomplish this? I've seen examples where the canDeactivate function within the guard class is passed one component argument, but I'm not sure how to pass the currently routed component.

charlitos
  • 105
  • 2
  • 10

3 Answers3

3

describe canDeactivate interface

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

describe guard

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanDeactivateComponent> {
  canDeactivate(component: CanDeactivateComponent) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

describe route

 path: 'yourPath',     
 canDeactivate: [CanDeactivateGuard],
 component: YourComponent

and component:

 ...
 class YourComponent implements CanDeactivateComponent {
 ...
   canDeactivate() { 
     ... everything you need to detect if you can leave route: return false, 
       or observable
   }
Andreq Frenkel
  • 1,188
  • 9
  • 14
  • I see, so Angular guard is already aware of the "current" component because it is defined in the route configuration. In other words, the argument that gets passed into the canDeactivate function in your "describe guard" is the compoment defined in your "describe route" code (`YourComponent`)? – charlitos Aug 31 '18 at 15:29
  • That's right. If you have nested component, you have to lift up such logic encapsulated inside children to the parent component. – Andreq Frenkel Aug 31 '18 at 15:31
0

You can try using an IEdited interface:

interface IEdited {
  hasBeenEdited: (): boolean
}

and have your components implement it, and then maybe this will work:

@Injectable()
class CanDeactivateEdited implements CanDeactivate<IEdited> {
  canDeactivate(
    component: IEdited,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState: RouterStateSnapshot
  ): boolean {
    return component.hasBeenEdited();
  }
}
yoavmatchulsky
  • 2,962
  • 1
  • 17
  • 22
0

I am using Angular 5, (not that I know it matters!) however the accepted answer was not sufficient for me, I learnt that we need to "configure the guard in application routing module using providers attribute of @NgModule decorator" , source: https://www.concretepage.com/angular-2/angular-candeactivate-guard-example

So I had to do add provider like below in app-routing.module.ts, in addition to the accepted answer:

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers:[CustomExitGuard]
})
AlpharettaTechy
  • 350
  • 2
  • 14