BLOG

ブログ

【Angular】ルーティングにGuardを追加

こちらは、Mavs Advent Calendar 2024 12日目の記事です🐺!

最近業務でAngularを使用しているので、ルーティングで使用すると便利な機能、ガードについてご紹介したいと思います!

🌲🌲🌲

ガードとは

Angularのガードは、ユーザーが特定のルートにアクセスする前に認証や権限のチェックを行うことができる機能になります。

Angularで利用可能なガードは4種類あります。

ガード名機能
CanActivate特定のルートへの遷移時に遷移可能か判定します。
CanActivateChild子ルートへの遷移時に遷移可能か判定します。
CanDeactivate現在のルートを離れる前に実行されます。未保存の変更がある場合に警告を表示するなど…
CanLoadモジュールがロード可能かを判定します。

では行きましょう!

実装例

実装の流れは以下になります!

  • ガードを作成

  ↓

  • ルートに追加

ではさっそく遷移可能かどうかの判定をするガードを作成しちゃいます!
以下は、AM9:00〜PM5:00までの時間帯にのみアクセスを許可する簡単な例です。
返却値はtrueかfalseになります。

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

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

  constructor(private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const now = new Date();
    const hour = now.getHours();

    if (hour >= 6 && hour < 22) {
      return true;
    } else {
      this.router.navigate(['/not-allowed']);
      return false;
    }
  }
}

CanActivateのルート設定

では先ほど作成したTimeGuardをルートに設定します。
今回はHomeComponentに遷移する前にAM6:00〜PM10:00の時間帯かどうかを判定したいと思います!

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { ParentComponent } from './parent/parent.component';
import { TimeGuard } from './time.guard';

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [TimeGuard] // ガードを適用
  },
  {
    path: 'parent',
    component: ParentComponent,
    children: {
      { path: 'child', component: ChildComponent }
    }
  },
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  providers: [TimeGuard],
  exports: [RouterModule]
})
export class AppRoutingModule { }

簡単ですね!

CanActivateChildのルート設定

CanActivateChildも流れは同じです!今回は一番最初に作成したCanActivateガードの内容はそのまま、インターフェースをCanActivateChildに書き換えたいと思います!

import { Injectable } from '@angular/core';
import { CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
// インターフェースをCanActivateChildに変更
export class TimeGuard implements CanActivateChild {

  constructor(private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    const now = new Date();
    const hour = now.getHours();

    if (hour >= 6 && hour < 22) {
      return true;
    } else {
      this.router.navigate(['/not-allowed']);
      return false;
    }
  }
}

では同じくルートに追加します。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { ParentComponent } from './parent/parent.component';
import { ChildComponent } from './child/child.component';
import { TimeGuard } from './time.guard';

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [TimeGuard]
  },
  {
    path: 'parent',
    component: ParentComponent,
    canActivateChild: [TimeGuard], // ガードを適用
    children: {
      { path: 'child', component: ChildComponent }
    }
  },
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  providers: [TimeGuard],
  exports: [RouterModule]
})
export class AppRoutingModule { }

また今回は別々にガードを作成しましたが、1つにまとめることもできます!

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

@Injectable({
  providedIn: 'root'
})
// CanActivateとCanActivateChildを設定
export class TimeGuard implements CanActivate, CanActivateChild {

  constructor(private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    // canActivateで判定する処理
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    // canActivateChildで判定する処理
  }
}

CanDeactivateのルート設定

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { ParentComponent } from './parent/parent.component';
import { ChildComponent } from './child/child.component';
import { FormComponent } from './form/form.component';
import { TimeGuard } from './time.guard';
import { CanDeactivateGuard } from './can-deactivate.guard'; // 追加

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [TimeGuard]
  },
  {
    path: 'parent',
    component: ParentComponent,
    canActivateChild: [TimeGuard],
    children: {
      { path: 'child', component: ChildComponent }
    }
  },
  {
    path: 'form',
    component: FormComponent,
    canDeactivate: [CanDeactivateGuard], // ガードを適用
  },
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  providers: [TimeGuard],
  exports: [RouterModule]
})
export class AppRoutingModule { }

CanLoadのルート設定

以下はCanLoadガードを使用して、認証されていないユーザーが特定のモジュールをロードできないようにしています。

  • ガード作成
import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class CanLoadGuard implements CanLoad {

  constructor(private authService: AuthService, private router: Router) {}

  canLoad(
    route: Route,
    segments: UrlSegment[]
  ): Observable<boolean> | Promise<boolean> | boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}
  • 認証サービス(ログイン可否)
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor() { }

  isLoggedIn(): boolean {
    return localStorage.getItem('authToken');
  }
}
  • ルートに追加
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CanLoadGuard } from './can-load.guard';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
    canLoad: [CanLoadGuard]
  },
  { path: '', redirectTo: '/home', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

まとめ

Angularのガードについて簡単にですがまとめてみました。

お役に立てたら嬉しいです!ではまた〜!

RELATED ARTICLE