← 解説「ルートガード — 入ってよいかを判定する」に戻る
サンプルコードで身につける: ルートガード — 入ってよいかを判定する
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1最小の CanActivateFn
ガードの骨組みだけを示した最小例です。CanActivateFn 型の関数を作り、true を返せば遷移が許可されます。まだ判定ロジックはありませんが、「ガード=遷移前に呼ばれて可否を返す関数」という形をまず押さえるための例です。
import { CanActivateFn } from '@angular/router';
// 遷移の直前に呼ばれ、true を返せば遷移が続行される
export const demoGuard: CanActivateFn = () => {
console.log('遷移しようとしています');
return true;
};
// ルート定義側:
// { path: 'mypage', component: MyPage, canActivate: [demoGuard] }
2ログインチェックとリダイレクト
認証ガードの定番形です。ガードはDIコンテキストで呼ばれるため、関数の中で inject() が使えます。未ログイン時は false ではなく router.parseUrl('/login') を返すことで、遷移を止めるだけでなくログイン画面へ誘導できます。
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
// 関数だが inject() が使える
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLoggedIn()) {
return true;
}
// UrlTree を返すと、そのURLへリダイレクトされる
return router.parseUrl('/login');
};
31つのガードを複数のルートで使い回す
ガードは関数なので、canActivate の配列に書くだけで何ルートにでも付けられます。「ログインが必要なページ」が増えても、ガード自体は1つ書けば済みます。各コンポーネントに認証チェックをコピーして回る必要がないのが、ルート側で守る利点です。
import { Routes } from '@angular/router';
import { authGuard } from './auth.guard';
import { Login } from './login';
import { MyPage } from './my-page';
import { OrderHistory } from './order-history';
export const routes: Routes = [
// ログイン画面自体にはガードを付けない
{ path: 'login', component: Login },
// ログインが必要なページに同じガードを並べる
{ path: 'mypage', component: MyPage, canActivate: [authGuard] },
{ path: 'orders', component: OrderHistory, canActivate: [authGuard] },
];
4遷移先の情報を使って権限を判定する
CanActivateFn は引数として遷移先のルート情報(ActivatedRouteSnapshot)を受け取れます。ルート定義の data に必要な権限を書いておき、ガード側でそれを読んで判定する、役割ベースの権限チェックの定番パターンです。1つのガードでページごとに異なる条件を扱えます。
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
// 第1引数で遷移先のルート情報を受け取れる
export const roleGuard: CanActivateFn = (route) => {
const auth = inject(AuthService);
const router = inject(Router);
// ルート定義の data に書いた必要権限を読む
const requiredRole = route.data['role'] as string;
if (auth.hasRole(requiredRole)) {
return true;
}
return router.parseUrl('/forbidden');
};
// ルート定義側:
// { path: 'admin', component: Admin,
// canActivate: [roleGuard], data: { role: 'admin' } }
5canDeactivate で「離れてよいか」を確認する
ガードには「入ってよいか」だけでなく「離れてよいか」を判定する canDeactivate もあります。編集中のフォームから離れる前に確認ダイアログを出す、データ消失防止の定番です。コンポーネント側に「保存済みか」を返すメソッドを用意し、ガードから呼び出します。
import { CanDeactivateFn } from '@angular/router';
// 編集画面が実装すべきインターフェース
export interface HasUnsavedChanges {
hasUnsavedChanges(): boolean;
}
// 離れる直前に呼ばれる。false を返すと遷移が止まる
export const unsavedChangesGuard: CanDeactivateFn<HasUnsavedChanges> = (
component
) => {
if (component.hasUnsavedChanges()) {
return confirm('保存していない変更があります。移動しますか?');
}
return true;
};
// ルート定義側:
// { path: 'edit', component: EditPage,
// canDeactivate: [unsavedChangesGuard] }