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」を紹介します。