Angular CDK Layoutを触ってみる

## はじめに

前々から、見てみようと思いながら手を付けられていなかった、Angular CDKを少し触ってみたので、メモを残しておこうと思います。

AngularがCDKが提供する機能、「CDKを学ぶべき理由」などに関しては@Quramyさんが[Angular Component Dev Kit 入門](https://qiita.com/Quramy/items/caf015e56d536411d4ba)という記事でわかりやすく説明されているので、そちらを見るとイメージが掴めるかと思います。

Angular CDKの中でも提供されているAPIがわかりやすいのと、ライブラリ開発者でなくても、アプリケーションの実装に直接使えそうな`Layout`を触ってみました。

公式ドキュメント: https://material.angular.io/cdk/layout/overview

## 準備

### インストール

```
$ ng new cdk-test
$ cd cdk-test
$ npm i @angular/cdk
```

Angular Materialを使っているプロジェクトでは、CDKはインストール済みのはずなので、インストールの必要はありません。

### CDKモジュールのインポート

app.moduleにLayoutModuleをインポートし、AppModuleのimportsに追加します。

```typescript:app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { LayoutModule } from '@angular/cdk/layout'; // 追加

import { AppComponent } from './app.component';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    LayoutModule, // 追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

ここまでで、CDK Layoutが提供する機能を利用する準備が整いました。

## BreakpointObserver

BreakpointObserverを使うと、事前に設定したブレイクポイントに対応する画面サイズの変更をObservableで受け取ることができます。
レスポンシブな画面を作る際に、`*ngIf`などを使っていると便利な機能です。

### 画面サイズの変更を通知するObservableを取得する

今回は、app.componentで直接BreakpointObserverを使ってみます。(実際に使う際には、サービスに分けたいところです)

```typescript:app.component.ts
import { Component, OnInit } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  isMobile$: Observable<boolean> = of(false);

  constructor(private breakpointObserver: BreakpointObserver) {}

  ngOnInit() {
    // BreakpointObserverから画面幅の変更を取得してBooleanに変換
    this.isMobile$ = this.breakpointObserver.observe(Breakpoints.XSmall).pipe(
      map((state: BreakpointState) => state.matches)
    );

    // consoleで確認
    this.isMobile$.subscribe((isMobile: boolean) => console.log('isMobile:', isMobile));
  }
}
```

* BreakpointObserverをDIする
* BreakpointObserver.observeにブレイクポイント情報を渡して変更を通知するObservableを取得

### ブレイクポイントをユーザー定義の値に設定

事前に用意されいているBreakpoints以外の値を設定するには、次のようにobserveにcssのメディアクエリの書式でブレイクポイントの情報を渡します。

```typescript
this.breakpointObserver.observe('(max-width: 768px)')...
```

最小と最大を厳密に指定するような場合には、配列で渡します。

```typescript
    this.breakpointObserver.observe([
      '(min-width: 480px)',
      '(max-width: 768px)',
    ])...
```

## MediaMatcher

MediaMatcher.matchMedia()はWindow.matchMedia()のラッパーみたいです。同期的にangularのclass内からCSSメディアクエリの条件を満たしているかの情報を取得することができます。

```typescript:app.component.ts
import { Component, OnInit } from '@angular/core';
import { MediaMatcher } from '@angular/cdk/layout';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  constructor(private mediaMatcher: MediaMatcher) {}

  ngOnInit() {
    // 戻り値はネイティブのMediaQueryList型を返す
    const mediaQueryList = this.mediaMatcher.matchMedia('(min-width: 1000px)');
    // consoleで確認
    console.log(mediaQueryList);
  }
}
```

BreakpointObserverの実装を見ると、MediaMatcher.matchMedia()で取得したMediaQueryListにaddListenerしている箇所が確認できました。
BreakpointObserverに比べて、こちらのAPIを直接使う場面はあまりないように思います。

## 終わりに

かなりざっくりかつ、まとまりのない内容になってしまいましたが、Angular CDK Layout(特にBreakpointObserver)の雰囲気を掴んでもらえれば良いかなと思います。
他のCDKモジュールの機能についても触っていきたいと思っています。