AngularのGuard使用時に、MatDialogでユーザに通知する


はじめに

権限を持ったユーザのみページアクセスの許可を出したいときに、AngularではGuardが提供されています。

AngularでGuardの導入

Guardはページ遷移の直前に動作し、アクセス権限を持ったユーザの判断をします。
ログイン認証などで使われますが、例えば会員と非会員ごとにアクセス可能なページを切り分けたい場合などにも使えます。

その際に、アクセスできなかったユーザに対して「なぜアクセスできないのか」を通知するのは重要だろうと考え、MatDialogと組み合わせて実装してみました。

アプリケーションの構成

この記事ではGuardとDialogをどのように組み合わせたのかを紹介するために、認証済みでないユーザに対し、ログインが必要である旨を伝えます。

以下が構成です。

このアプリケーションでは、AuthGardにSubjectを持たせて、ページ遷移に失敗した場合にnextでfalseを流します。

LoginComponent側ではsubscribeをしておき、Guadの結果を受け取ることでDialogを表示する、と言う流れにしています。

まずはGuadのコードです。

auth-guard.ts


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

  private authenticationSubject = new Subject<boolean>();

  constructor(
    private session$: SessionService,
    private router: Router,
  ) {}

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

      if () {
        // some code
      }

      // ページ遷移失敗時の結果を流す
      this.authenticationSubject.next(false);
      this.router.navigateByUrl('login');
  }

  get authentication$() {
    return this.authenticationSubject.asObservable();
  }

}

login-component.ts

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit, OnDestroy {

  onDestroy$ = new Subject<any>();

  constructor(
    private initialGuard: InitializeGuard,
    private dialog: MatDialog
  ) { }

  ngOnInit() {

    // guard の結果を購読
    this.initialGuard
      .authentication$
      .pipe(takeUntil(this.onDestroy$))
      .subscribe( result => {

        if (!result) {
          const dialog = this.dialog.open(AlertComponent, {
            data: {
              title: 'ログインしてください',
              button: '閉じる',
            }
          })

          // 購読の破棄
          dialog.afterClosed().subscribe( result => this.onDestroy$.next());
        } 
      })
  }

今回はダイアログの表示とGuardからの結果を受け取る箇所のみ記載しています。
FormGroupなどは割愛しています。

また、AlertDialogはComponent間で使い回しをしており、Component側からメッセージを入力させるように設計しているため、このような実装になっています。

ログイン画面はこのようにしています。

未認証アクセスボタンはHomeComponentへのリンクを埋め込んでいます。
認証が住んでいない状態でクリックすると、このようなダイアログが表示されます。

今回のようにGuardからの結果を各コンポーネントで購読するのは冗長なので、共通コンポーネントを挟んでおくと良さそうです。

まとめ

これを使えば、非会員ユーザなどに対しての通知も簡単に実装できそうです。

私はこの方法しか思いつきませんでしたが、他にも思いついた方がいらっしゃればコメントをお願いいたします。