Angular Material の "Datepicker" の見た目と戦う

Angular Material で Datepicker を使おうとした時のお話です。
Angular 自体は 4 です。

Datepicker の Document は以下。
https://material.angular.io/components/datepicker/overview

デフォルトだと以下の絵のように `<input>` に表示されるテキストが `M/d/yyyy` になるようです。
更に曜日の表記も馴染みがない。
この見た目を変更するべく調査したのです。

![20170928120737_e.png](https://qiita-image-store.s3.amazonaws.com/0/53393/9464c0d0-4e7f-49f3-5f0e-4c9852fd65ba.png)


## まずは、デフォルト状態

```html
<md-form-field>
  <input mdInput [mdDatepicker]="picker" placeholder="Choose a date">
  <md-datepicker-toggle mdSuffix [for]="picker"></md-datepicker-toggle>
  <md-datepicker #picker></md-datepicker>
</md-form-field>
```

そのままコピペしたわけですが、
最初の絵のように普通に使える Datepicker なのですが、
`<input>` に表示されるテキストが `M/d/yyyy` になっています。

フォーマットを変えたい！
何かフォーマットを指定する手段はないものか？

## ロケールを指定

https://material.angular.io/components/datepicker/overview#setting-the-locale-code

```diff
  import { Component, OnInit } from '@angular/core';
+ import { DateAdapter, NativeDateAdapter } from '@angular/material';

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

-   constructor() {
+   constructor(dateAdapter: DateAdapter<NativeDateAdapter>) {
+     dateAdapter.setLocale('de-DE');
    }

    ngOnInit() {
    }
  }
```

最初に見つけたのはロケール指定。
日本っぽいカレンダーになってくれるだろうか？
ということで早速やってみる。
もちろん `dateAdapter.setLocale('ja');` である。

![20170928120834e.png](https://qiita-image-store.s3.amazonaws.com/0/53393/41e9ecf8-b191-ceeb-505e-b0d472bc3464.png)

こんな感じの絵になった。
`yyyy/M/d` のようであるが良い感じだ。
曜日も `日 月 火...` と良い感じだ。
ついでに Year の表記も `SEP 2017` から `2017年9月` に、
Month の表記も `SEP` から `9月` に変わって日本らしくなった。


しかし、全部の日にちに "日" が付いている・・・。
なんとも気持ち悪い。
(執筆時には見慣れてて気持ち悪さ半減しておりましたが...)

これより先を弄るとなると Provider を上書きしてやる必要がありそうです。
https://material.angular.io/components/datepicker/overview#customizing-the-date-implementation
https://material.angular.io/components/datepicker/overview#customizing-the-parse-and-display-formats
https://material.angular.io/components/datepicker/overview#localizing-labels-and-messages

## とりあえず Google Developer Tools

![20170930222520.png](https://qiita-image-store.s3.amazonaws.com/0/53393/34e4fd4c-5206-17d9-ce66-eaf64c554f66.png)

`mat-calendar-body-cell-content` か...

### calendar-body っぽいところを探す

https://github.com/angular/material2/blob/master/src/lib/datepicker/calendar-body.html#L42
にありました。

どうやら `{{item.displayValue}}` が、実際の値を表示しているようです。

これを追っていくと...

- `*ngFor="let item of row;`
    - item は row の要素
- `*ngFor="let row of rows;`
    - row は rows の要素
- rows はHTML上から見つからなかったので ts 側で変数となっているはず
    - https://github.com/angular/material2/blob/master/src/lib/datepicker/calendar-body.ts#L52
    - ありました。 `selector: '[mat-calendar-body]'` の `@Input() rows: MatCalendarCell[][];` のようですね。

`@Input()` ということは、どこかで設定している人がいるはず！

### mat-calendar-body の [rows] を探す

https://github.com/angular/material2/blob/master/src/lib/datepicker/month-view.html#L9
にいました。

次は `_weeks` だ！
さっきと同じく ts 側にいるはずだ！

### _weeks を探す

https://github.com/angular/material2/blob/master/src/lib/datepicker/month-view.ts#L171
ここで値を突っ込んでいました。

おもむろに `console.log(dateNames[i])` をしてみると...
```
CHIBI: 1日
CHIBI: 2日
CHIBI: 3日
CHIBI: 4日
CHIBI: 5日
```

それっぽい！
dateNames[i] の代わりに適当な文字列を入れてみても...

![20170928120834.png](https://qiita-image-store.s3.amazonaws.com/0/53393/089f0107-7445-818a-b10a-0cc6eaddb1c5.png)

これだ...!!

dateNames は以下のコードで取得されている。
https://github.com/angular/material2/blob/master/src/lib/datepicker/month-view.ts#L157

DateAdapter が出てきた。
さっきさらっと流した Provider 上書きの出番！
https://material.angular.io/components/datepicker/overview#customizing-the-date-implementation

## Provider 上書き

```diff
  import { Component, OnInit } from '@angular/core';
  import { DateAdapter, NativeDateAdapter } from '@angular/material';

+ class MyDateAdapter extends NativeDateAdapter {
+   getDateNames(): string[] {
+     const dateNames: string[] = [];
+     for (let i = 0; i < 31; i++) {
+       dateNames[i] = String(i + 1);
+     }
+     return dateNames;
+   }
+ }

  @Component({
    selector: 'datepicker-example',
    templateUrl: './datepicker-example.component.html',
    styleUrls: ['./datepicker-example.component.css'],
+   providers: [
+     {provide: DateAdapter, useClass: MyDateAdapter}
+   ]
  })
  export class DatepickerExampleComponent implements OnInit {


    constructor(dateAdapter: DateAdapter<NativeDateAdapter>) {
      dateAdapter.setLocale('ja');
    }

    ngOnInit() {
    }
  }
```

## :tada::tada::tada:

![20170928120928.png](https://qiita-image-store.s3.amazonaws.com/0/53393/25bd0b23-28cb-bd5e-22f1-f59ca2e5b11a.png)

## 最後にmemo

https://stackoverflow.com/questions/44452966/angular-2-material-2-datepicker-date-format

上記 Stack Overflow を見ると `MD_DATE_FORMATS` をいじったり
NativeDateAdapter の `format()` をオーバーライドしたりしている。
いまいち良くわからないまま試してみたものの、上手くいかず今回の方法に落ち着きました。

何かしら使うかもしれないので、とりあえずリンクだけ残す。
特に format は `yyyy/MM/dd` な形式にするときに使いそうだ。