Angular Materialの利用例
以下では、Angular MaterialのさまざまなUI部品からいくつか抜粋して、利用例を紹介していきます
入力フォーム用のUI部品を表示
Angular Materialが提供する、テキストボックスやチェックボックスなどの入力フォーム用UI部品を、図3のサンプルで紹介します。それぞれのUI部品が、Material Design固有の外観や挙動(クリック時のアニメーションなど)を備えています。
図3のUI部品は、リスト4の通り記述されています。
<h3>テキストボックス</h3><!--(1)-->
<mat-form-field>
<input matInput placeholder="名前">
</mat-form-field>
<h3>セレクトボックス</h3><!--(2)-->
<mat-form-field>
<mat-label>得意な言語</mat-label>
<mat-select placeholder="select">
<mat-option value="c">C/C++</mat-option>
<mat-option value="java">Java</mat-option>
<mat-option value="javascript">JavaScript</mat-option>
<mat-option value="swift">Swift</mat-option>
<mat-option value="kotlin">Kotlin</mat-option>
</mat-select>
</mat-form-field>
<h3>チェックボックス</h3><!--(3)-->
<mat-checkbox>Angularの経験あり</mat-checkbox>
<h3>スイッチ</h3><!--(4)-->
<mat-slide-toggle>この情報を公開する</mat-slide-toggle>
<h3>ラジオボタン</h3><!--(5)-->
<mat-radio-group>
<mat-radio-button value="1" checked>iOS</mat-radio-button>
<mat-radio-button value="2">Android</mat-radio-button>
</mat-radio-group>
<h3>ボタン</h3><!--(6)-->
<div class="button-container">
<button mat-button>通常のボタン</button>
<button mat-raised-button color="primary">影付きボタン</button>
<button mat-stroked-button color="primary">枠線ボタン</button>
</div>
テキストボックス(1)は、HTMLの<input>にmatInput属性(ディレクティブ)を設定して、Material Designを子要素に適用する機能を持つ<mat-form-field>で囲みます。複数行テキストボックス(<textarea>)も同様にできます。
セレクトボックス(2)は、<mat-select>、<mat-option>で記述します。なお、Angular 7からは、HTMLの<select>や<option>でもMaterial Designのセレクトボックスを記述できるようになりました(連載第18回「フレームワークとツールの両面で完成度アップ! 「Angular」バージョン7の新機能」参照)。
チェックボックス(3)は<mat-checkbox>で記述します。同じ機能をスイッチで実現する<mat-slide-toggle>(4)も利用できます。また、ラジオボタン(5)は、複数の<mat-radio-button>を、<mat-radio-group>で囲みます。
ボタン(6)は、<button>にmat-button属性(ディレクティブ)を指定します。指定するディレクティブの種類でデザインが変化し、mat-raised-buttonなら影付きのボタン、mat-stroked-buttonなら枠線で囲まれたボタンになります。詳細は公式ドキュメントも参考にしてください。
選択できるサイドメニューを表示
Angular Materialでは、ページ内要素のアニメーションや表示切り替えを行う機能が提供されています。ここではそのうち、Sidenavコンポーネントで実装したサイドメニューのサンプル(図4)を紹介します。ボタンをクリックすると左からサイドメニューが表示され、リストをクリックするとサイドメニューが閉じて、選択内容が画面に表示されます。
ルートコンポーネントのテンプレートはリスト5の通りです。
<mat-sidenav-container><!-- 全体をまとめるコンテナー ...(1)-->
<mat-sidenav #nav mode="side"><!-- サイドメニュー ...(2)-->
<mat-toolbar color="primary">
<span>選択肢</span>
</mat-toolbar>
<mat-action-list> <!-- リスト表示 ...(3)-->
<button *ngFor="let elem of listData" mat-list-item
(click)="listSelected(elem);">{{elem}}</button>
</mat-action-list>
</mat-sidenav>
<mat-sidenav-content> <!-- ページ本体 ...(4)-->
<mat-toolbar color="primary">
<span>Sidenavのサンプル</span>
</mat-toolbar>
<div class="content">
<!-- ボタン ...(5)-->
<button mat-raised-button color="primary" (click)="nav.open();">
好きなスマホメーカーは?
</button>
<div>あなたの選択は:{{result}}</div><!-- 結果表示 ...(6)-->
</div>
</mat-sidenav-content>
</mat-sidenav-container>
サイドメニューに対応する<mat-sidenav>(2)と、ページ本体に対応する<mat-sidenav-content>(4)を、<mat-sidenav-container>(1)の子要素にして記述します。
<mat-sidenav>の属性に指定した「#nav」は、他の箇所から「nav」という名前で参照できるようにする指定です。また、mode属性はサイドメニューの動作モードで、「side」を指定すると、サイドメニューとページ本体を並べて表示します。ほかに、サイドメニューがページ本体にオーバーレイする「over」、サイドメニューがページ本体を右に押し出して表示する「push」の動作モードが指定できます。
サイドメニュー内の<mat-action-list>(3)は、リストを表示するコンポーネントです。listData配列の内容を*ngForディレクティブで繰り返してリストに表示します。
ページ本体には、サイドメニューを表示するボタン(5)と、選択結果の表示部(6)を記述します。ボタンクリック時、nav.openメソッドでサイドメニューを開きます。
リスト5のテンプレートに対応する実装はリスト6の通りです。
export class AppComponent {
listData = [ // リストに表示する選択肢 ...(1)
'Apple',
(略)
];
result: string = ''; // 選択結果 ...(2)
@ViewChild("nav") nav: MatSidenav; // Sidenav ...(3)
listSelected(value) { // リスト選択時の処理 ...(4)
this.nav.close();
this.result = value;
}
}
(1)はサイドメニューのリストに表示する選択肢、(2)は選択結果を格納する変数です。(3)は、ViewChildデコレーターを利用して、リスト5(2)のサイドメニューをnav変数で参照する記述です。リストクリック時の処理は(4)で、nav.closeメソッドでサイドメニューを閉じて、選択結果をresult変数に格納します。resultの内容はリスト5(6)で画面表示されます。
ダイアログを表示
Angular Materialには、ダイアログやメッセージをWebページにオーバーレイして表示させる機能があります。本記事では、ダイアログのサンプル(図5)を説明します。名前を入力してボタンを押すとダイアログに名前が表示され、ダイアログに好きな食べ物を入力してOKボタンを押すと、ダイアログでの入力内容が元のページに反映されます。
ルートコンポーネントのテンプレートはリスト7の通りです。(1)で、テキストボックスとダイアログ表示ボタンを記述します。(2)は、ダイアログから渡された入力結果(dialogAnswer)に値が設定されている場合だけ、回答を表示する記述です。
<h3>質問</h3><!--(1)-->
<mat-form-field>
<input matInput placeholder="名前" [(ngModel)]="name">
</mat-form-field>
<div>
<button mat-raised-button color="primary" (click)="showDialog();">
質問ダイアログを表示
</button>
</div>
<div *ngIf="dialogAnswer"><!--(2)-->
<h3>回答</h3>
{{name}}さんは「{{dialogAnswer}}」と回答しました。
</div>
ダイアログ表示などの処理は、ルートコンポーネントにリスト8の通り実装します。
export class AppComponent {
name: string = ''; // 入力する名前 ...(1)
dialogAnswer: string = ''; // ダイアログから受け取る回答 ...(2)
constructor(private dialog: MatDialog) { } // コンストラクター ...(3)
showDialog() { // ボタンクリック時の処理 ...(4)
// ダイアログを表示 ...(5)
const dialogRef = this.dialog.open(MyDialogComponent, {
width: '300px',
data: { name: this.name }
});
// 表示したダイアログが閉じられた時の処理 ...(6)
dialogRef.afterClosed().subscribe(result => {
this.dialogAnswer = result;
});
}
}
(1)は画面で入力する名前、(2)はダイアログから受け取る回答を格納する変数です。コンストラクター(3)で、ダイアログを操作するMatDialogのオブジェクトdialogを、依存性注入で受け取っておきます。
ボタンクリック時の処理(4)では、まず、dialog.openメソッド(5)でダイアログを表示します。第1引数は表示するダイアログのコンポーネント(後述)、第2引数はオプションで、ここではwidth(ダイアログの幅)とdata(ダイアログに渡すデータ)を指定します。dataには、画面で入力した名前nameを設定します。
dialog.openメソッドの戻り値dialogRefは、表示されたダイアログに対応しており、メソッドを実行してダイアログに追加の設定ができます。ここではdialogRef.afterClosedメソッド(6)で、ダイアログが閉じられた時の処理を設定します。イベントハンドラーの変数resultには、ダイアログから渡された値が格納されるので、それをthis.dialogAnswerに代入して画面に表示させます。
ダイアログ自体を表すMyDialogComponentコンポーネントのテンプレートは、リスト9の通りです。
<h1 mat-dialog-title>{{data.name}}さんへ</h1><!-- タイトル ...(1)-->
<div mat-dialog-content><!-- ダイアログ内容 ...(2)-->
<mat-form-field>
<input matInput placeholder="好きな食べ物" [(ngModel)]="returnValue">
</mat-form-field>
<p>これでよろしいですか?</p>
</div>
<div mat-dialog-actions><!-- ダイアログ操作部 ...(3)-->
<!-- キャンセルボタン ...(4)-->
<button mat-button (click)="closeDialog();" cdkFocusInitial>
キャンセル
</button>
<!-- OKボタン ...(5)-->
<button mat-button [mat-dialog-close]="returnValue">
OK
</button>
</div>
(1)がダイアログのタイトル、(2)が内容、(3)が操作部です。HTML要素にそれぞれ、mat-dialog-title、mat-dialog-content、mat-dialog-actions属性(ディレクティブ)を指定して、ダイアログのデザインを適用します。
(4)はキャンセルボタンで、後述するcloseDialogメソッドを実行してダイアログを閉じます。(5)はOKボタンです。「[mat-dialog-close]="returnValue"」と設定しておくと、ボタンクリック時にresultValue変数の値がダイアログの呼び元(この場合、リスト8(6)のresult変数)に渡されます。
MyDialogComponentコンポーネントの実装はリスト10の通りです。
export class MyDialogComponent {
constructor( // コンストラクター ...(1)
private dialogRef: MatDialogRef<MyDialogComponent>, // ダイアログ ...(2)
@Inject(MAT_DIALOG_DATA) private data: any // 渡されたデータ ...(3)
) { }
closeDialog() { // ダイアログを閉じる処理 ...(4)
this.dialogRef.close();
}
}
コンストラクター(1)で、ダイアログに対応するdialogRef(2)と、呼び出し元から渡されたデータdata(3)を、依存性注入で受け取ります。呼び元から渡されるデータは、MAT_DIALOG_DATAという名前(DIトークン)で依存性注入されるため、受け取る時に「@Inject(MAT_DIALOG_DATA)」と指定します。キャンセルボタンでダイアログを閉じる処理(4)では、dialogRef.closeメソッドでダイアログを閉じます。
[補足]Angular MaterialとCDK
Angular Materialでは、デザインに依存しない挙動や機能を、CDK(The Component Dev Kit)として別に提供しています。CDKの詳細は、公式ページを参照してください。CDKの利用例として、ドラッグアンドドロップやスクロールを実現する方法を、連載第18回「フレームワークとツールの両面で完成度アップ! 「Angular」バージョン7の新機能」で紹介しています。
まとめ
本記事では、Googleのデザインガイドライン「Material Design」に対応したAngularのUI部品コンポーネント「Angular Material」を紹介しました。Angular Materialを利用すれば、WebページをMaterial Designに対応させて、見た目や使い勝手を向上できます。
次回は、CSSフレームワークBootstrapに準拠したUI部品をAngularに提供する「Angular powered Bootstrap」を紹介します。
