1

Is there any way a component can dynamically register the canDeactivate guard for itself? Its mainly to prompt/stop the user from navigating with confirmation like Yes/No.

I'm trying to do this because the application in question is quite large and workflow components (example: payment, transfer, etc) are spread across many different modules in the app. I am not sure if registering them statically in the router config would be a good idea.

I am looking for a way where in the component can prompt and control the navigation similar to guards.

Mateusz Kocz
  • 4,492
  • 1
  • 25
  • 27
ashish.gd
  • 1,713
  • 1
  • 14
  • 25

2 Answers2

1

Components cannot register router guards, however the most common canDeactivate implementation rely on a component method to be defined.

A striped down version can be:

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

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

Basically if a component exposes a canDeactivate method then it will be used to control the deactivation, otherwise the router navigation would just continue.

I would register a similar guard on your top level component, and let single components implement or not the specific method.

  • 1
    Exposing the `canDeactivate` method doesn't really help as it doesn't seem to be called on route change. The only way it will get called is the component/service implementing it is statically registered with the routes configuration. – ashish.gd Sep 26 '18 at 21:58
  • Simple stackblitz example for the above comment: https://stackblitz.com/edit/angular-candeactivate-component – ashish.gd Sep 26 '18 at 21:58
  • My answer definitely wasn't clear enough. 1. Define a `canDeactivate` guard service like the above 2. Register the `canDeactivate` guard on your components 3. Expose a `canDeactivate` method on the components you want the guard to be working In your example you simply added a `canDeactivate` method in a component, there's no service defined nor a routing `canDeactivate` guard set, that's why it's not invoked. – Massimiliano Sartoretto Sep 27 '18 at 07:04
  • My bad! I understood your answer better once I read this https://www.squeed.com/2017/12/18/how-to-use-a-custom-dialog-with-the-candeactivate-route-guard-in-angular/ However, am still looking for a way if we can avoid registering the `canDeactivate` guard for every route that needs it, instead the component can dynamically register it on certain conditions. In current approach, we need to register guards statically at the time of development (even with the service based approach). One alternate option I feel is updating route config at runtime, but not sure if that's right. – ashish.gd Sep 27 '18 at 15:45
  • I believe you cannot dynamically register a route guard, and honestly I cannot think of a use case for that anyway. However you don't need to specify the guard for every route, you can just create a parent route and set it there, as specified here https://stackoverflow.com/a/43487962/4544288 (or just use your top-level component). – Massimiliano Sartoretto Sep 27 '18 at 16:10
  • 1
    My application in question is large and spread across quite a few modules. Hence rather than registering statically in every module's specific routes, we're looking for a way to control in a implicit manner. Also, registering at a parent route does not trigger the guard for child routes. It needs 1:1 mapping for the `route:canDeactivate` guard to work! Btw, sample use case is workflow components like payments, transfers, etc. But you are right, as of now the only option we have is to register it for every route where needed. Cheers ! – ashish.gd Sep 27 '18 at 18:44
0

Now you can register canDeactivate router guard easily for your component. Just be sured it gets own path in a routing and then use this way to add a guard:

this.activatedRoute.routeConfig!.canDeactivate = [...your guards];

ActivatedRoute you can get through DI either by constructor or inject function.

Anton Marinenko
  • 2,749
  • 1
  • 10
  • 16