Home > Enterprise >  Generic CanDeactivate Implementation not getting called
Generic CanDeactivate Implementation not getting called

Time:11-17

I´m trying to implement a guard to prevent user navigation to the login page once they authenticate themselves. I want to use this guard for every page component in my app except the login page itself.

Here goes the code I´m trying:

import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree, CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';


@Injectable({ providedIn: 'root' })
export class AuthGuard<T> implements CanActivate, CanDeactivate<T> {

  constructor(private router: Router, private auth: AuthenticationService) {
  }

  canActivate(
    _route: ActivatedRouteSnapshot,
    _state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    return this.auth.isAuthenticated() || this.router.navigate(['/login']);
  }

  canDeactivate(
    _component: T,
    _currentRoute: ActivatedRouteSnapshot,
    _currentState: RouterStateSnapshot,
    nextState: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {

    if (nextState && nextState.url.includes('/login')) {
      return !this.auth.isAuthenticated();
    }

    return true;
  }

}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';

const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [AuthGuard],
    canDeactivate: [AuthGuard]
  },
  {
    path: '**',
    redirectTo: 'home',
  }
];


@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [AuthGuard]
})
export class AppRoutingModule { }

I´ve also tried implementing a new canActive guard just for the login component, which works, but I don´t like this approach as much.

I´ve seen some examples like this one https://www.concretepage.com/angular-2/angular-candeactivate-guard-example. Using an interface, in this case, doesn´t seem right because typescript doesn´t support default method implementation. I would rather not repeat the validation on every component.

Why doesn´t my implementation get called? How should I go about doing this?

Any suggestions will be appreciated.

Aditional info: Angular v14.1.3

Sample: https://stackblitz.com/edit/angular-ivy-hvz4mc

CodePudding user response:

You have two injectable decorators in auth guard, please find below the stackblitz working.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';

@Injectable()
// @Injectable({ providedIn: 'root' }) // <-- mistake here
export class AuthGuard implements CanActivate, CanDeactivate<any> {

  constructor(private router: Router, private auth: AuthenticationService) {
  }

forked stackblitz

  • Related