noxi雑記

.NET、Angularまわりの小ネタブログ

Angular の Guard で別のページに遷移させる

Angular の Guard といえば、ページ遷移前に認証情報を取得したり、ページを表示出来るかの権限チェックをしたり、、、ページを表示する前の前処理を色々行う機能です。 Angular 6 までは、 Guard はページを表示できるできないの boolean の値を返すことしかできませんでしたが、 Angular 7 からは違うページを表示するために URL を返せるようになりました。

試した環境

この記事は以下のAngularバージョンで作成されています。

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.3.5
Node: 10.15.1
OS: darwin x64
Angular: 7.2.8
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.5
@angular-devkit/build-angular     0.13.5
@angular-devkit/build-optimizer   0.13.5
@angular-devkit/build-webpack     0.13.5
@angular-devkit/core              7.3.5
@angular-devkit/schematics        7.3.5
@angular/cli                      7.3.5
@ngtools/webpack                  7.3.5
@schematics/angular               7.3.5
@schematics/update                0.13.5
rxjs                              6.3.3
typescript                        3.2.4
webpack                           4.29.0

Guard の作成

Guard は標準で Angular CLI に生成コマンドが含まれていますので、サクッと作成します。

ng-guard-sample tnakatani$ ng generate guard authorize
? Which interfaces would you like to implement? CanActivate
CREATE src/app/authorize.guard.ts (461 bytes)

標準で生成される Guard の実装はこんな感じです。テンプレートを作ってくれるだけでもとてもありがたいです。
また Angular 6 時代とは異なり、戻りの型として UrlTree を返せるようになっていることが分かります。

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthorizeGuard implements CanActivate {
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }

}

Guard から別のページに遷移させる

Guard から true/false ではなく UrlTree を返すことで、本来の遷移先 URL ではなく別の URL へ遷移させることができます。
UrlTreeRouter.createUrlTree から生成します。生成時のパラメーターは Router で遷移するときと同じです。
例えばランダムに 1/2 の確率でログインページに飛ばすような雑な Guard だと、次の実装のようになります。

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthorizeGuard implements CanActivate {

  constructor(private readonly _router: Router) {
  }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    const value = Math.random();
    if (value > 0.5) {
      return true;
    }

    return this._router.createUrlTree(['authorize', 'login'], { queryParams: { reason: 'random' } });
  }

}