メディアクエリをangularで設定するディレクティブ

どこかに誰か書いてそうで、意外となかったので、まとめてみたり。
（でも、まだangularよ〜わからん！な未熟者だったりするので(というか、jsが使いこなせない)、なんか微妙だったらご指摘ください。）

#やりたいこと
テンプレートになんか書くことで、スクリーンサイズに応じて表示非表示を切り替える部分を切り分けたい

```html:雰囲気
<template *mediaquery="720px以下">
720px以下で表示したい
</template>
<template *mediaquery="そうじゃないサイズで表示したい">
721px以上で表示したい
</template>
```


#ディレクティブを定義するファイルの作成

angular cliを利用しての作成は↓


```cmd:angulr-cli
ng g directive app以下ファイルパス/ディレクティブファイル名
```
今回は`app/directive`にディレクティブを整理した構成で、`mediaquery`という名前で作成しようと思うので
`ng g directive directive/mediaquery`になる。
作成すると、ファイルが作成されるとともに、app.moduleに登録される。

```app.module.ts
import { MediaqueryDirective } from './directive/mediaquery.directive';

@NgModule({
  declarations: [
    AppComponent,
    ~略~
    MediaqueryDirective,
  ],
  ~略~
})
~略~

```


#ディレクティブの実装

##利用元から、引数を受け取る

Inputoオブジェクトを利用できるようにして、利用元からの引数（？）を受け取れるようにする。

```mediaquery.directive.ts
import { Directive, Input } from '@angular/core';

@Directive({
  selector: '[appMediaquery]'
})
export class MediaqueryDirective {

  @Input('appMediaquery') mediaquery: string;

  constructor() { }

}
```

ここで、`@Input('appMediaquery') mediaquery: string;`にすると、

```error
Uncaught Error: Template parse errors:
Can't bind to 'appMediaquery' since it isn't a known property of 'div'.
```
というエラーが発生！ということで、注意。（まだまだわかってないまま作成しております。）


##テンプレートを扱うオジェクトを追加

テンプレートにした要素を挿入するためのオブジェクトを記載。

```mediaquery.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appMediaquery]'
})
export class MediaqueryDirective {

  @Input() mediaquery: string;

    constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef ) {
  }

}
```

##値を受け取って何かするための下準備

`OnChanges`メソッドを利用できるようにする＆テンプレートを埋め込むメソッドを記入。
とりあえず、何の条件もつけずに、consoleで正しく動くか＆テンプレートが表示されるかお試し。

```mediaquery.directive.ts

import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';

~略~
export class MediaqueryDirective implements OnChanges {

  ~略~

  ngOnChanges() {
    console.log('abc');
    if ( this.mediaquery === 'hoge') {
      console.log('def');
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
      console.log('ghi');
      this.viewContainer.clear();
    }
  }
}

```

```hoge.component.html
<div *appMediaquery='hoge'>aaaaa</div>
//↑「*」はテンプレートを再利用できるものにするディレクティブの利用を表すらしい。
//ディレクティブの　selector　に定義してあるものを記載
```

##条件を作る

sassのmediaquery mixinにあわわせる感じで、breakpointの変数を登録

手前味噌な参照：メディアクエリをsassで設定するmixin
https://qiita.com/niever66/items/dae7799b1ebec1881c25

```mediaquery.directive.ts
export class MediaqueryDirective implements OnChanges {

  ~略~

  breakpoints = {
    xs: 520,
    sm: 768,
    md: 1024,
    lg: 1366,
    xl: 1920
  };
}
```

渡された文字列を元に条件文を作成
※コピペで変に重複したコード記載していたので、重複箇所削除（2019.5.7）

```mediaquery.directive.ts
export class MediaqueryDirective implements OnChanges {

  ~略~

  ngOnChanges() {
    let operator: string;
    let pt = this.mediaquery.split(/[a-z]/);
    for ( let i = 0; i < pt.length; i++ ) {
      if( pt[i] ){
        operator = pt[i];
        break;
      }
    }

    let bp: string;
    let mq = this.mediaquery.split(operator);
    for ( let i = 0; i < mq.length; i++ ) {
      if( mq[i] ){
        bp = mq[i];
        break;
      }
    }

    switch (operator) {
      case '<':
        if (window.innerWidth < this.breakpoints[bp]) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
        break;
      case '>':
        if (window.innerWidth > this.breakpoints[bp]) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
        break;
      case '=':
        if (window.innerWidth === this.breakpoints[bp]) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
        break;
      case '<=':
      case '=<':
        if (window.innerWidth <= this.breakpoints[bp]) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
        break;
      case '>=':
      case '=>':
        if (window.innerWidth >= this.breakpoints[bp]) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
        break;
      default:
        this.viewContainer.clear();
      }

  }
}
```

#テンプレートから呼び出す

```template.component.html
<div *appMediaquery="'<=sm'">smサイズより小さい時に表示</div>
```

#とりま完成！

ところで、これだとまだ　min:hoge and max:fuge 的な 間の時っていうのができませんな。