Search code examples
angularauth-guardcanactivatecandeactivatecanactivatechild

Angular's "CanActivate" interface is deprecated. How to replace it?


My Angular app includes a simple AuthGuard as shown below and there has never been a problem with it. Recently, I upgraded my Angular version from 15.1.4 to 15.2.0 and since then, my IDE indicates that both CanActivate and CanActivateChild are deprecated.

The official Angular documentation for CanActivate says:

Deprecated: Use plain JavaScript functions instead.

How would I need to adjust the code below to get rid of the deprecated warning?

export class AuthGuard implements CanActivate, CanActivateChild {

    constructor(private authService: AuthenticationService) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree  {
        return this.authService.checkLogin()
            .pipe(
                map(() => true),
                catchError(() => {
                    this.router.navigate(['route-to-fallback-page']);
                    return of(false);
                }
            )
        );
    }

    canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        return this.canActivate(route, state);
    }
}

Solution

  • v18 Answer:

    CanActivate has been un-deprecated in v18. See this PR

    I'd still recommend to migrate to CanActivateFn.


    Pre v18 answer.

    The trick is to rely on inject() for the injection of the instances you need :

    export const canActivate: CanActivateFn = (
      route: ActivatedRouteSnapshot,
      state: RouterStateSnapshot
    ) => {
      const authService = inject(AuthenticationService);
      const router = inject(Router);
    
      return authService.checkLogin().pipe(
        map(() => true),
        catchError(() => {
          return router.createUrlTree(['route-to-fallback-page']);
        })
      );
    };
    
    export const canActivateChild: CanActivateChildFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => canActivate(route, state);
    

    inject() allows you to use the dependency injection when you are in an injection context. For example in a constructor or like here when passing a function to a core Angular feature.

    You can also read about it on the deprecation list.

    Also Since v16, Angular provides some helper functions to convert classes to functionnal guards like mapToCanActivate :

    @Injectable({providedIn: 'root'})
    export class AdminGuard {
      canActivate() {
        return true;
      }
    }
    
    const route: Route = {
      path: 'admin',
      canActivate: mapToCanActivate([AdminGuard]),
    };
    

    You can find the others here.