記事の概要
Angularで認証済みの場合のみ遷移させたい画面があり、ルーティングにGuardを適用するためにGuardクラスを作成したところ、implementsしているCanActivate
がDeprecatedでマークされていることに気づきました。
調べてみると、Angular15.2からクラスベースのルートGuardは非推奨になっていることがわかりました。
代わりに関数ベースのGuardを使うことが推奨されていますが、日本語の情報が少なかったため書き方を備忘としてメモしておきます。
ただし関数ベースのGuardといっても全種類ではなくCanActivateFn
に限っての説明となります。
先に結論:書き方
関数ベースGuardの実装サンプル:
export const authGuard: CanActivateFn = () => {
// 認証管理サービスのinject
const authService = inject(AuthService);
// Routerのinject
const router = inject(Router);
if (authService.isAuthenticated()) {
// 認証済みのため(認証後画面への)ナビゲーションOK
return true;
} else {
// 認証未済のため認証ページに飛ばす
return router.parseUrl('auth');
}
};
- ~.routes.tsに直接定義してももちろん大丈夫です。
-
CanActivateFn
のパラメータroute: ActivatedRouteSnapshot, state: RouterStateSnapshot
は非使用のため省略しています。
Routes定義の実装サンプル:
export const routes: Routes = [
{
path: 'after-auth', // 認証後ページ
loadComponent: () => import('./pages/after-auth/after-auth.page').then(m => m.AfterAuthPage),
canActivate: [authGuard] // Guardの登録
},
{
path: 'auth', // 認証ページ
loadComponent: () => import('./pages/auth/auth.page').then(m => m.AuthPage),
}
];
CanActivate:
に配列でGuardを登録したら実装完了です。
CanAcvivateFnについて
CanAcvivate
の代わりに実装するべきCanActivateFn
について見てみます。
型エイリアスはこのようになっています。基本的にはboolean
かUrlTree
(orこれらのObservable, Promise)を返す関数を実装すれば良いです。
パラメータのActivatedRouteSnapshot, RouterStateSnapshotは必須ではありません。
type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
説明も見てみます。
If all guards return true, navigation continues. If any guard returns false, navigation is cancelled. If any guard returns a UrlTree, the current navigation is cancelled and a new navigation begins to the UrlTree returned from the guard.
「全てのguardがtrue
を返した場合はナビゲーション続行。いずれかのguardがfalse
を返したらナビゲーションはキャンセル。いずれかのguardがUrlTree
を返したら、それまでのナビゲーションはキャンセルされてUrlTree
へのナビゲーションがスタートする。」
・・・ということのようです。
元々のナビゲーションを許容する場合は単にtrue
を返却すれば良いです。
一方で、元々のナビゲーションをキャンセルする場合、代わりに遷移させたいパスがあることがほとんどだと思います。
この場合の実装として、例えば以下のような感じで紹介している記事を見かけます。
/** 中略 */
const router = inject(Router)
if (canNavigate()) {
return true;
else {
// 代わりのパスにナビゲートした上で元々のナビゲーションはキャンセル
router.navigate(['auth']);
return false;
}
Router.navigate
した上でfalse
を返すこれでも動きますが、副作用を起こしている感が若干キレイじゃない気がしてしまいます。 (関数ベースだと特に)
代わりにUrlTreeを返す(Router.parseUrl()
)方がキレイに同じことを実現できるとこちらの記事で知ったため、サンプル実装でもそうしています。
/** 中略 */
const router = inject(Router)
if (canNavigate()) {
return true;
else {
// 代わりのパスにナビゲートした上で元々のナビゲーションはキャンセル
return router.parseUrl('auth');
}
おまけ:UrlTreeについて
UrlTree
というクラスについて聞いたことが無かったためついでに少し調べてみました。
UrlTree is a data structure that provides a lot of affordances in dealing with URLs
「UrlTreeはURLに関する様々な利便性を提供するデータ構造」のようです。
Usage notesがとてもわかりやすいため引用します。
@Component({templateUrl:'template.html'})
class MyComponent {
constructor(router: Router) {
const tree: UrlTree =
router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
const f = tree.fragment; // return 'fragment'
const q = tree.queryParams; // returns {debug: 'true'}
const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
g.children['support'].segments; // return 1 segment 'help'
}
}
様々なプロパティによって、パラメータなどURLを構成するパーツにアクセスできるようです。
また、UrlTreeを作成する手段としていくつかあるようで:
-
Router
クラスのparseUrl(url: string): UrlTree;
はURL文字列をシンプルにパースしてUrlTreeを作成できるようです。( https://angular.jp/api/router/Router#parseurl ) - 同じく
Router
クラスのcreateUrlTree(commands: any[], navigationExtras?: UrlCreationOptions): UrlTree;
はより柔軟にUrlTreeを組み立てられるようです。( https://angular.jp/api/router/Router#createurltree )
パラメータ等を付与したURLに遷移させる場合はこちらを使うのが便利かと思いました。
まとめ
関数ベースGuard(CanActivateFn)の実装方法とUrlTreeについて紹介しました。
最近のAngularやRxJSで非推奨になっている機能や代わりに推奨されている書き方について、意外と日本語の記事が少なかったりするため、少しでもこの記事が役に立てば嬉しいです。
参考資料