Angular CanActivate Guard like CanDeactivate

I wondering why is the CanActivate guard different from CanDeactivate guard.

CanActivate

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable({ providedIn: 'root' })
export class CanActivateGuard implements CanActivate {
  public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return true;
  }
}

In this guard you just can check the route and use some of your services.
Great, but wait … check out the CanDeactivate guard.

CanDeactivate

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';

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

@Injectable({ providedIn: 'root' })
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  public canDeactivate(component: CanComponentDeactivate): Promise<boolean> | boolean {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

In this guard you have to set a component. It’s annoying but in my case I really want it. I come from Ionic 3 and need these guard because Ionic 4 does not have ionViewCanEnter and ionViewCanLeave. Therefore I created this guard, so you can simple add the method canDeactive() to every component / page you have. Simple as ionViewCanLeave. Really awesome. BUT I also want it for the CanActivate.

Is there a way to use CanActivate in the same way as CanDeactivate? For the activate guard, I also want to simple use canActivate() methods in my components / pages. Like:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

export interface CanComponentActivate {
  canActivate: () => Promise<boolean> | boolean;
}

@Injectable({ providedIn: 'root' })
export class CanActivateGuard implements CanActivate<CanComponentActivate> {
  public canActivate(component: CanComponentActivate): Promise<boolean> | boolean {
    return component.canActivate ? component.canActivate() : true;
  }
}

Just exactly like the deactivate guard. But this does not work.

Heres my app-routing.module.ts (Snippet)

 { path: 'test', canActivate: [CanActivateGuard], canDeactivate: [CanDeactivateGuard], component: TestPage}

TestPage

@Component({
  selector: 'app-test-page',
  templateUrl: 'test.page.html',
  styleUrls: ['test.page.scss']
})
export class TestPage {
  public canActivate(): boolean {
    console.log('Sure, come in.');
    return true;
  }

  public canDeactivate(): boolean {
    console.log('Muahahah, never. :P');
    return false;
  }
}

The CanActivateguard does not know the instance of the component. Because it is not load. It a guard before the component load. CanDeactivate knows the instance.

TL;DR: I have to create a CanActivate guard for each page I want and resolve the boolean over services. I have no access to the components instance. … Only the component itself. I could consider static methods. But then I have problems to use services. … :thinking:

FYI

You do not have to create a component anymore to use CanDeactivate on a page as shown here

1 Like