Skip to content

Angular CanActivate 非推奨化対応

非推奨化の概要
Angular 15.2 以降、CanActivateCanActivateChild インターフェースは非推奨となりました。代わりに関数ベースのルートガードの実装が推奨されるようになりました。

問題の背景

Angular 15.1.4 から 15.2.0 へのアップグレードにより、CanActivateCanActivateChild インターフェースが非推奨となりました。公式ドキュメントでは 「プレーンな JavaScript 関数を代わりに使用すること」 が推奨されています。

典型的なクラスベースの AuthGuard 実装(非推奨):

ts
export class AuthGuard implements CanActivate, CanActivateChild {

    constructor(private authService: AuthenticationService) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        // 認証チェックロジック
    }

    canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this.canActivate(route, state);
    }
}

v18 での変更点

Angular v18 で CanActivate は非推奨状態から解除されましたが、それでも関数ベースのガード(CanActivateFn)が推奨されています。

関数ベースガードへの移行方法

基本実装パターン

inject() 関数を使用した依存性の注入で関数ベースガードを実装:

ts
import { CanActivateFn, CanActivateChildFn } from '@angular/router';
import { inject } from '@angular/core';

export const authGuard: CanActivateFn = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
) => {
  const authService = inject(AuthenticationService);
  const router = inject(Router);

  return authService.checkLogin().pipe(
    map(() => true),
    catchError(() => {
      // エラー時のURL遷移
      return of(router.createUrlTree(['/login']));
    })
  );
};

export const authChildGuard: CanActivateChildFn = 
  (route, state) => authGuard(route, state);

変更ポイントの詳細

  1. クラスから関数へ

    • AuthGuard クラスを削除
    • CanActivateFn 型の関数をexport
  2. 依存性の注入

    • コンストラクタインジェクションを inject() に変更
    ts
    // 従来
    constructor(private authService: AuthService)
    
    // 新しい方法
    const authService = inject(AuthService);
  3. エラーハンドリング

    diff
    - this.router.navigate(['/login']);
    + return of(router.createUrlTree(['/login']));

ルーティング設定での使用法

ts
const routes: Routes = [
  {
    path: 'protected',
    component: ProtectedComponent,
    canActivate: [authGuard], // 関数ベースガードを直接指定
    children: [
      { 
        path: 'child',
        component: ChildComponent,
        canActivateChild: [authChildGuard]
      }
    ]
  }
];

代替アプローチ

既存クラスを利用した移行

CanActivateFn で既存ガードクラスのメソッドを呼び出す方法:

ts
@Injectable({ providedIn: 'root' })
class AuthGuardService {
  constructor(private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    // 従来の実装ロジック
  }
}

export const authGuard: CanActivateFn = 
  (next, state) => inject(AuthGuardService).canActivate(next, state);

関連メソッドのグループ化

複数のガード関数を名前空間で整理:

ts
export namespace AuthGuards {
  export const canActivate: CanActivateFn = (route, state) => {
    // 実装
  };
  
  export const canActivateChild: CanActivateChildFn = 
    (route, state) => canActivate(route, state);
}

// ルーティングでの使用例
{ path: 'admin', canActivate: [AuthGuards.canActivate] }

ベストプラクティス

変換用ヘルパー関数の活用

Angular 16 以降では組み込みの変換関数が利用可能:

ts
import { mapToCanActivate } from '@angular/router';

@Injectable({providedIn: 'root'})
export class AdminGuard {
  canActivate() {
    // 認証ロジック
  }
}

const adminRoute: Route = {
  path: 'admin',
  canActivate: mapToCanActivate([AdminGuard]), // クラスを関数に変換
};

推奨されるエラーハンドリング

UrlTree を使用したリダイレクトが公式推奨:

ts
// 非推奨: 明示的なナビゲーション
router.navigate(['/login']);

// 推奨: UrlTreeの返却
return router.createUrlTree(['/login']);

パフォーマンス上の利点

  • ツリーシェイキング: 未使用ガードの自動削除
  • 起動時間短縮: 関数ベースガードの軽量性
  • メモリ使用量削減: 不要なインスタンス生成の回避

移行時の注意点

  1. inject() はコンテキスト内でのみ使用可能

    ts
    // エラー: 非注入コンテキスト
    const invalidUse = () => {
      const service = inject(MyService); // エラー発生!
    };
  2. CanActivateChildFn は既存の実装を流用可能

    ts
    export const canActivateChild: CanActivateChildFn = 
      (route, state) => canActivate(route, state);
  3. ガード関数の命名は任意(従来とは異なる)

    ts
    // 従来: [AuthGuard]
    // 新規: [authGuard] あるいは [authGuardFunction]

最後に: Angularの関数ベースガード移行は公式推奨の方向性です。より軽量でコンテナフレンドリーなアプローチにより、現代的なAngularアプリケーション開発に適応した設計となります。