Angular CanActivate 非推奨化対応
非推奨化の概要
Angular 15.2 以降、CanActivate
とCanActivateChild
インターフェースは非推奨となりました。代わりに関数ベースのルートガードの実装が推奨されるようになりました。
問題の背景
Angular 15.1.4 から 15.2.0 へのアップグレードにより、CanActivate
と CanActivateChild
インターフェースが非推奨となりました。公式ドキュメントでは 「プレーンな JavaScript 関数を代わりに使用すること」 が推奨されています。
典型的なクラスベースの AuthGuard 実装(非推奨):
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()
関数を使用した依存性の注入で関数ベースガードを実装:
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);
変更ポイントの詳細
クラスから関数へ
AuthGuard
クラスを削除CanActivateFn
型の関数をexport
依存性の注入
- コンストラクタインジェクションを
inject()
に変更
ts// 従来 constructor(private authService: AuthService) // 新しい方法 const authService = inject(AuthService);
- コンストラクタインジェクションを
エラーハンドリング
diff- this.router.navigate(['/login']); + return of(router.createUrlTree(['/login']));
ルーティング設定での使用法
const routes: Routes = [
{
path: 'protected',
component: ProtectedComponent,
canActivate: [authGuard], // 関数ベースガードを直接指定
children: [
{
path: 'child',
component: ChildComponent,
canActivateChild: [authChildGuard]
}
]
}
];
代替アプローチ
既存クラスを利用した移行
CanActivateFn
で既存ガードクラスのメソッドを呼び出す方法:
@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);
関連メソッドのグループ化
複数のガード関数を名前空間で整理:
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 以降では組み込みの変換関数が利用可能:
import { mapToCanActivate } from '@angular/router';
@Injectable({providedIn: 'root'})
export class AdminGuard {
canActivate() {
// 認証ロジック
}
}
const adminRoute: Route = {
path: 'admin',
canActivate: mapToCanActivate([AdminGuard]), // クラスを関数に変換
};
推奨されるエラーハンドリング
UrlTree
を使用したリダイレクトが公式推奨:
// 非推奨: 明示的なナビゲーション
router.navigate(['/login']);
// 推奨: UrlTreeの返却
return router.createUrlTree(['/login']);
パフォーマンス上の利点
- ツリーシェイキング: 未使用ガードの自動削除
- 起動時間短縮: 関数ベースガードの軽量性
- メモリ使用量削減: 不要なインスタンス生成の回避
移行時の注意点
inject()
はコンテキスト内でのみ使用可能ts// エラー: 非注入コンテキスト const invalidUse = () => { const service = inject(MyService); // エラー発生! };
CanActivateChildFn
は既存の実装を流用可能tsexport const canActivateChild: CanActivateChildFn = (route, state) => canActivate(route, state);
ガード関数の命名は任意(従来とは異なる)
ts// 従来: [AuthGuard] // 新規: [authGuard] あるいは [authGuardFunction]
最後に: Angularの関数ベースガード移行は公式推奨の方向性です。より軽量でコンテナフレンドリーなアプローチにより、現代的なAngularアプリケーション開発に適応した設計となります。