angular material componentsの使用

angular materialを使用して簡単なプログラムを作成してみました。都道府県のリストデータをCRUDするプログラムの作例です。殆どが[Angular Material](https://material.angular.io/)のExampleベースのコードですが、自分の思うよう動作をさせるにあたり、調査・コーディングした部分も有るのでブログに載せてみます。
※DB連動していないため、追加変更したデータは保持されません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/94957/3aa9ec3a-a504-39e6-7508-35502d50cb8e.png)

上記のテーブルに加え、ダイアログボックスを実装してみます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/94957/f8a5d7c2-9c12-7b17-f67a-ff08c230f150.png)

[Angular Material](https://material.angular.io/)のStart upに従いプロジェクトを作成し、各コンポーネントの利用方法はExampleを参考にしています。ソースは[stackblitz](https://stackblitz.com/edit/angular-mat-2tp4hi)にアップしてあり、動作確認可能です。

##環境
Angular CLI: 7.0.7
Node: 8.11.4
OS: windows8.1 x64
Angular: 7.0.4

Package                           Version
@angular-devkit/core              7.0.7
@angular-devkit/schematics        7.0.7
@angular/cdk                      7.1.1
@angular/cli                      7.0.7
@angular/material                 7.1.1
@ngtools/webpack                  7.0.7
@schematics/angular               7.0.7
@schematics/update                0.10.7
rxjs                              6.3.3
typescript                        3.1.6
webpack                           4.19.1

使用したmaterialコンポーネントは以下です。

BrowserAnimationsModule
    MatInputModule
    MatButtonModule
    MatTableModule
    MatCheckboxModule
    MatDialogModule
    MatPaginatorModule
    MatSortModule
    MatSelectModule

#アプリケーションコンポーネント
| コンポーネント/サービス|説明 |material|
|:--|:-:|:-:|
| PrefViewComponent|table表示,選択,フィルター,ソート,ページング|MatTableModule,MatCheckboxModule,MatPaginatorModule,MatSortModule,MatSelectModule|
| PrefViewEditComponent|編集ボタン,Dialog呼出,データ削除|MatButtonModule,MatDialogModule|
| PrefViewEditDialogComponent|Dialog表示,データ追加・更新|MatDialogModule,MatInputModule,MatButtonModule|
| prefService|データ保持,コンポーネント連携||

以下アプリケーションコンポーネントのポイント部分のみ、抜粋して説明します。

### PrefViewComponent
##### 単一行選択の実装
matCheckBoxがバインドしているSelectionModelの生成にて、コンストラクタ第一引数（選択モード指定）に単一(false)を指定します。

```typescript:pref-view.component.ts
  selection = new SelectionModel<PrefItem>(false,[]);

```
checkBox変更のイベントをリスニングするため、同SelectionModelのエミッターを宣言してsubscribeにコールバック関数を設定します。

```typescript:pref-view.component.ts
  private cbEmmiter=this.selection.onChange.asObservable();

  ngOnInit() {
    this.cbEmmiter.subscribe(cb=>{
      if(cb.source.selected.length > 0){
        this.service.selectPerfItem=cb.source.selected[0];
      }else{
        this.service.selectPerfItem=null;
      }
    });
  }
```
#####列ソート ページング
MatSort, MatPaginatorを使用することで簡単に実装できます。実装方法はExampleの通りです。作例ではtableのdatasourceが非同期に更新されるので、その契機で同コンポーネントを再バインドする必要があります。該当の処理をngAfterViewInitに書きます。

```typescript:pref-view.component.ts

  ngAfterViewInit(){
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

```
また、datasourceバインドの契機にも同処理を書いておきます。

```typescript:pref-view.component.ts
  updateData(){
    //tableのデータソース更新
    this.dataSource= new MatTableDataSource<PrefItem>(this.service.data);
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }
```

#####フィルター
Exampleをそのまま流用です。MatTableDataSourceのfilterメソッドを使用しています。

### PrefViewEditDialogComponent
親コンポーネントと切り離れた方が可読性が良いと思い、独立コンポーネントとして実装ました。これにより１つのコンポーネントに縛られることなく、複数のコンポーネントで共有することができます。サービスから動作モードを取得して、追加・更新ダイアログを共通にしています。
MatInputの数値表示をカンマ区切りとしたいため、changeイベントで変換をかけています。

```typescript:pref-edit-dialog.component.ts
   onChangeArea(){
    this.editItem$.prefArea=Number(this.perfArea$.replace(/,/g,''));
    this.perfArea$=formatNumber(this.editItem$.prefArea,this.locale,'.2');
  }
```
Dialog上でデータの追加・更新を処理しており、処理後の通知をサービスのObservableに流しています。他コンポーネントはサービスのObservableをsubscribeすることで変更通知を受信することができます。この仕組みによってtableコンポーネントは表示を更新します。

#最後に
materialを使ってみた感触ですが、あまり突っかかることなく使えました（気持ち良く使える）。特に気に入っているところはDialogの使いやすさです。コンポーネント化すれば、どのコンポーネントからも呼べるし、別途作成したコンポーネントをDialog上に配置することもできます。疎結合デザインのメリットを実感できるUIですね。