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


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

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

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

まずは、デフォルト状態

<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 になっています。

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

ロケールを指定

  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'); である。

こんな感じの絵になった。
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

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

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

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

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

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

@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] の代わりに適当な文字列を入れてみても...

これだ...!!

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 上書き

  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() {
    }
  }

最後にmemo

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

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