はじめに
ずっとないと思い込んでいたけど.....モーダルとトースト普通にあるんすね、Angular Material。
ということでCSSフレームワークのAngular Materialを使ってモーダルとトーストを実装していきます
環境
本記事における実行環境と使用ライブラリについて
$ ng version Angular CLI: 8.3.20 Node: 12.13.1 OS: darwin x64 Angular: 8.2.14 ... animations, common, compiler, compiler-cli, core, forms ... language-service, platform-browser, platform-browser-dynamic ... router Package Version ----------------------------------------------------------- @angular-devkit/architect 0.803.20 @angular-devkit/build-angular 0.803.20 @angular-devkit/build-optimizer 0.803.20 @angular-devkit/build-webpack 0.803.20 @angular-devkit/core 8.3.20 @angular-devkit/schematics 8.3.20 @angular/cdk 7.3.7 @angular/cli 8.3.20 @angular/fire 5.2.3 @angular/material 7.3.7 @ngtools/webpack 8.3.20 @schematics/angular 8.3.20 @schematics/update 0.803.20 rxjs 6.5.3 typescript 3.5.3 webpack 4.39.2
実装
作成中のメモアプリにAngular Materialを使用して、下記2点の機能を実装します。
- モーダル
- トースト
モーダル
Angular MaterialのDialogを使って、 リンクをクリックするとモーダルでフォルダの新規作成のフォームを表示する機能を作成します。
AppModuleの設定1
モーダルを使用する事前準備として、AppModuleのimports:[]
にMatDialogModule
を追加します。
- src/app/app.module.ts
// ...省略 import { MatDialogModule } from '@angular/material/dialog'; @NgModule({ //... 省略 imports: [ MatDialogModule, ], bootstrap: [AppComponent] }) export class AppModule {}
モーダルのコンポーネントを作成
モーダルのコンポーネントを作成していきます。
まずは、コンポーネントクラスのファイルを作成します。
- src/app/component/folder-create-modal/folder-create-modal.component.ts
import { Component, OnInit } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; /** * フォルダ新規作成モーダルのコンポーネントクラス * * @export * @class FolderCreateModalComponent * @implements {OnInit} */ @Component({ selector: 'app-folder-create-modal', templateUrl: './folder-create-modal.component.html', styleUrls: ['./folder-create-modal.component.scss'] }) export class FolderCreateModalComponent implements OnInit { // モーダルへの参照をDI constructor(public dialogRef: MatDialogRef<FolderCreateModalComponent>) {} ngOnInit() {} /** * モーダルの作成ボタン押下時に呼び出し * * @memberof FolderCreateModalComponent */ public onOkClick(): void { // モーダルを閉じる this.dialogRef.close(); // TODO: フォルダ作成処理追加 } /** * モーダルのキャンセルボタン押下時に呼び出し * * @memberof FolderCreateModalComponent */ public onNoClick(): void { // モーダルを閉じる this.dialogRef.close(); } }
constructor(public dialogRef: MatDialogRef<FolderCreateModalComponent>) {}
でモーダルへの参照変数をDIしています。このモーダルへの参照変数を使用して、ボタン押下時にモーダルを閉じる処理を実装しています。
続いて、モーダルのテンプレートファイル(HTML)です。
mat-dialog-actions align="end"
でモーダルに表示するボタンを右寄せにしています。
- src/app/component/folder-create-modal/folder-create-modal.component.html
<h1 mat-dialog-title>新規フォルダを作成</h1> <div mat-dialog-content> <p>フォルダは、同じテーマや目的をもつメモをまとめて整理するのに役立ちます。</p> <mat-form-field class="folder"> <input matInput class="title" placeholder="名前" /> </mat-form-field> </div> <div mat-dialog-actions align="end"> <button mat-button (click)="onNoClick()">キャンセル</button> <button mat-button cdkFocusInitial (click)="onOkClick()">作成</button> </div>
モーダルのレイアウトを設定しているSCSSファイルです。特に特記事項がないため、このファイルについては説明をスキップします。
- src/app/component/folder-create-modal/folder-create-modal.component.scss
.folder { width: 100%; }
モーダルを表示する側の実装
モーダルを表示するリンクを実装するコンポーネントを作成します。
- src/app/component/forder-list-header/forder-list-header.component.ts
import { MatDialog } from '@angular/material/dialog'; // ...省略 export class ForderListHeaderComponent implements OnInit { // MatDialogをDI constructor(public dialog: MatDialog) {} /** * フォルダの新規作成モーダルを表示する * * @memberof ForderListHeaderComponent */ public create() { console.log('create'); const dialogRef = this.dialog.open(FolderCreateModalComponent, { width: '360px' }); dialogRef.afterClosed().subscribe(() => { console.log('The dialog was closed'); }); } }
フォルダ新規作成リンクをクリックした時にcreate()
メソッドを呼び出して、Dialogのopen
でモーダルを表示しています。
なお、モーダルを表示する機能を実装しているコンポーネントであるため、constructor
でMatDialog
をDIする必要があります。
モーダルを表示する側のコンポーネントのテンプレートファイルです。新規フォルダリンクを実装しています。
- src/app/component/forder-list-header/forder-list-header.component.html
<div class="header"> <div class="title">フォルダ</div> <a (click)="create()" class="add-link"> <mat-icon class="icon">create_new_folder</mat-icon> <span>新規フォルダ</span> </a> </div>
AppModuleの設定2
最後に、AppModuleのentryComponents:[]
に今回作成したモーダルのコンポーネントであるFolderCreateModalComponent
を追加してモーダルの実装は完了です。
- src/app/app.module.ts
// ...省略 import { FolderCreateModalComponent } from './component/folder-create-modal/folder-create-modal.component'; @NgModule({ // ...省略 entryComponents: [FolderCreateModalComponent], // ...省略 }) export class AppModule {}
トースト
Angular MaterialのSnackbarを使って、 ログアウトが成功した時にログアウトが完了した旨をトーストでユーザーに知らせる機能を作成します。
AppModuleの設定
モーダルを使用する事前準備として、AppModuleのimports:[]
にMatSnackBarModule
を追加します。
- src/app/app.module.ts
// ...省略 import { MatSnackBarModule } from '@angular/material/snack-bar'; @NgModule({ // ...省略 imports: [ MatSnackBarModule ], }) export class AppModule {}
トースト用のサービスクラスを作成
トーストを表示するためのサービスクラスを作成します。もちろんサービスを作成せずにトーストを表示したいコンポーネントに直接処理を描いても問題ありません。
- src/app/services/toast.service.ts
import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; /** * トーストの表示を制御するサービス * * @export * @class ToastService */ @Injectable({ providedIn: 'root' }) export class ToastService { // tslint:disable-next-line:variable-name private _actionMessage = 'Close'; // tslint:disable-next-line:variable-name constructor(private _snackBar: MatSnackBar) {} /** * トーストを表示する * * @param {string} message トーストに表示するメッセージ * @memberof ToastService */ public open(message: string): void { // 2000ミリ秒間トーストを表示する this._snackBar.open(message, this._actionMessage, { duration: 2000 }); } }
open
メソッドでトーストを表示しています。なお、MatSnackBar
をDIする必要があります。
サービスクラスを経由してトーストを表示
最後に作成したToastService
をトーストを表示したいコンポーネントで呼び出す処理を書いてあげれば完了です。
- src/app/component/header/header.component.ts
import { ToastService } from '../../services/toast.service'; // ...省略 export class HeaderComponent implements OnInit { constructor( public afAuth: AngularFireAuth, private router: Router, private _toastService: ToastService, private spinnerService: SpinnerService ) {} /** * ログアウト処理 * * @memberof HeaderComponent */ public signOut(): void { this.spinnerService.show(); this.afAuth.auth .signOut() .then(() => { this.router.navigate(['/login']); // トーストを表示する this._toastService.open('ログアウトしました。'); }) .catch(error => { // トーストを表示する this._toastService.open('ログアウトに失敗しました。'); console.log(error); }) // 一連の処理が完了したらスピナーを消す .finally(() => this.spinnerService.hide()); } }