Angular Materialでマテリアルでレスポンシブなリストを実装する

**更新履歴**
2018.04.05 - [5.0.0-beta.13 (2018-02-22)](https://github.com/angular/flex-layout/blob/master/CHANGELOG.md#500-beta13-2018-02-22) におけるBREAKING CHANGESに対応

---

[Angular Material (material2)](https://material.angular.io/)は2017/10/06時点で未だbetaバージョンですが、レスポンシブルでマテリアルデザインなSPAを作る際にはドキュメントも充実しており手軽に美しく実現できて素敵です。

今回はAngular Materialの **[グリッドリスト](https://material.angular.io/components/grid-list/overview)** とangularの **[フレックスレイアウト](https://github.com/angular/flex-layout/wiki)** を使ってレスポンシブなデザインを構築しました。

## 環境
**[@angular](https://github.com/angular/angular)**: 4.4.3
**[@angular/material](https://github.com/angular/material2)**: 2.0.0-beta.11
**[@angular/flex-layout](https://github.com/angular/flex-layout)**: 2.0.0-beta.9

## 素のグリッドデザイン
Angular Materialで提供されているGrid listをそのまま使うと、幅にあわせて表示エリアが拡大・縮小します。

![angular-not-flexLayout_500p.gif](https://qiita-image-store.s3.amazonaws.com/0/167943/cd7a2d60-f708-3d81-2cec-83c70c6a05d3.gif)

このままの状態でもユーザの環境に合わせたレンダリングができるのですが、今回自分が実現したかったのは「表示領域にあわせてコンテンツ表示数を増やしたい」という要件でした。

これを実現するために、angularのflex-layoutを組み合わせて使います。

## FlexLayoutと組み合わせて使う
使い方は[ここ](https://github.com/angular/flex-layout/wiki)にあります。

### flex-layoutのインストール
angularプロジェクトで使用できるようにするためには `$ npm -i @angular/flex-layout --save` コマンドでインストールし、 `app.module.ts` でインポートします。

- app.module.ts

```ts
import { FlexLayoutModule } from '@angular/flex-layout';
..
  imports: [
    FlexLayoutModule,
```

### ブレークポイント
今回やりたいことをflex-layoutで実現するためには **[Breakpoints](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints)** を知っておく必要があります。

![layout-adaptive-breakpoints-01.png](https://qiita-image-store.s3.amazonaws.com/0/167943/a1f43d0a-41ca-df05-aaba-51cdd191efc5.png)

ディスプレイサイズに応じて名前付け(例:`600px - 959px` の場合は `sm`)がされており、その名前ごとに表示方法を指定することが出来ます。

- MediaQueries and Aliases

breakpoint | mediaQuery 
:-------:|----------
 xs | 'screen and (max-width: 599px)'
 sm | 'screen and (min-width: 600px) and (max-width: 959px)'
 md | 'screen and (min-width: 960px) and (max-width: 1279px)'
 lg | 'screen and (min-width: 1280px) and (max-width: 1919px)'
 xl | 'screen and (min-width: 1920px) and (max-width: 5000px)'

## フレックスレイアウトを使う
今回は複数要素を表示幅に応じて改行して表示させます。このような用途の場合、 fxLayoutWrapを使用します。

**fxLayoutWrap**
<font color="red">※ [5.0.0-beta13 (2018-02-22)](https://github.com/angular/flex-layout/blob/master/CHANGELOG.md#500-beta13-2018-02-22) の変更でfxLayoutWrapは廃止されました。2018年4月時点での新しいコード例はこのページの下記に追加しています。</font>

> Defines if child items appear on a single line or on multiple lines within a flexbox container. `<div fxLayoutWrap> </div>`

```
子アイテムがフレックスボックスコンテナ内の1行または複数行に表示されるかどうかを定義します。
```

簡単な使用例：

```html
<div fxLayoutWrap>
  <div fxFlex="25" fxFlex.sm="33.3" fxFlex.xs="50">
    <p>1st</p>
    <p>2nd</p>
    <p>3rd</p>
  </div>
</div>
```

`fxFlex="xx"` は、100に対する割合を設定します。値は 整数 or `%` or `px` 単位 で指定します。この例ではデフォルト=25(全体に対して1/4の幅)、幅が959px以下なら33.3(1/3の幅)、幅が599px以下なら50(1/2の幅)と指定しています。

## Grid Listを組み合わせる
このままでは幅が変わってもコンテンツのサイズが変わらないため、Grid Listを適用します。

※今回はGrid Listのスタイルを流用したいがために1コンテンツ1つしかないリストとして実装しています。本来の使い方とかけ離れているため推奨はしません。

この場合は `<mat-grid-list cols="1" rowHeight="1:1">` と指定します。（列:1, 縦横比1:1）

```html
<div fxLayoutWrap>
  <div fxFlex="25" fxFlex.sm="33.3" fxFlex.xs="50" *ngFor="let card of cards">
    <mat-grid-list cols="1" rowHeight="1:1">
      <mat-grid-tile>
        <mat-card>
          <img mat-card-image src="foo.jpg" alt="{{card.name}}'s latest card img.'">
        </mat-card>
        <mat-grid-tile-footer style="margin-bottom:48px;">
          <div class="name">{{card.name}}</div>
        </mat-grid-tile-footer>
        <mat-grid-tile-footer>
          <div class="kana">{{card.kana}}</div>
        </mat-grid-tile-footer>
      </mat-grid-tile>
    </mat-grid-list>
  </div>
</div>
```

※上の例では更にCardコンポーネントも併用しています。あと、足が2本生えていたりします…ごめんなさい。

## 完成したもの
![angular-flexLayout_500p.gif](https://qiita-image-store.s3.amazonaws.com/0/167943/08b76bbf-01d7-94ed-d45e-85c997709702.gif)

## まとめ
flex-layoutとAngular Materialの組み合わせでレスポンシブなコンテンツページを作ることができました。angularではまだまだ便利なAPIが提供されているので練習でどんどん使ってみて覚えていきたいと思います。

## 参考リンク
- [Layout Responsive UI - Magerial Design](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints)
- [angular](https://github.com/angular/angular)
- [Angular Material](https://material.angular.io/)
- [flex-layout Responsive API - angular/flex-layout](https://github.com/angular/flex-layout/wiki/Responsive-API)
- [作成したソースコード - Github](https://github.com/ne-peer/deredorm/blob/master/src/app/components/manage/dere-list/dere-list.component.html)
- [作ったサイト](http://deredorm.ne-peer.com) (2017/10/06 時点でこのデザインを適用中)

## 追記 2018.04.05
5.0.0-beta.13 (2018-02-22) で `fxLayoutWrap` は廃止されたため、次のように修正する必要があります。

_before_

```html
<div  fxLayout="row" fxLayoutWrap="wrap"> ... </div>
```

_after_

```html
<div  fxLayout="row wrap"> ... </div>
```

[5.0.0-beta.13 (2018-02-22) CHANGELOG](https://github.com/angular/flex-layout/blob/master/CHANGELOG.md#500-beta13-2018-02-22)