DatePipe を IE11 等で動かすと変な表示になる件に対応する

Angular で日付をフォーマット表示する場合、DatePipe を利用します。 Pipe とは、AngularJS で言う Filter で、構文も変わっていません。

<p>Date: {{ now | date:'yyyy-MM-dd HH:mm:ss' }}</p>

さて、このシンプルなフィルターですが、ブラウザごとに異なる表示になってしまいます。 以下の URL を開いてみてください。

https://plnkr.co/edit/lVCkJcIopxTOk5L9iyCm?p=preview

どうでしたか ? IE11, Edge の方は上手く表示されていないのではないかと思います。

Expected: 2017-04-01 12:34:56

Actual: 2017-04-01 12:00:4/1/2017 12:34:56 PM:4/1/2017 12:34:56 PM

どーやったらこんな表示になるんだ・・・。

どうやら不具合っぽい

もちろん、Issue は立っています。

date pipe formatter doesn't format minutes and seconds properly in IE 11.0.9600.18350 and Edge · Issue #9524 · angular/angular · GitHub

また、ドキュメントには以下の通り書いてあります。

DatePipe - ts - API

WARNINGS:

  • this pipe is marked as pure hence it will not be re-evaluated when the input is mutated. Instead users should treat the date as an immutable object and change the reference when the pipe needs to re-run (this is to avoid reformatting the date on every change detection run which would be an expensive operation).
  • this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera browsers.

重要なのは最後の 1 文。 DatePipe は I18n API を利用しているので、ChromeOpera でのみ信頼性があります。

Workaround: moment を利用する

Issue にもコメントがありますが、Workaround として以下 2 つが提示されています。

  • date:'short'date:'shortDate' はちゃんと動くよ !
  • moment を使った Pipe を定義しよう !

任意のフォーマットで表示したいのに date:'short' は動くと言われても困ります。

ということで、 moment を使った Pipe を定義しましょう。

1. npm install

はじめに、moment への依存設定を追加します。

npm install moment --save

なお、TypeScript の型定義は moment に含まれているため、別途 @types/moment を導入する必要はありません。(導入すると、追加しなくていいよと警告が表示されます。)

2. Pipe 実装

以下のように Pipe を実装します。

angular-cli をご利用の方は ng g pipe moment.pipe で雛形を生成できます。

import { Pipe, PipeTransform } from '@angular/core';
import * as moment from 'moment';  // (1)

@Pipe({
  name: 'moment' // (2)
})
export class MomentPipe implements PipeTransform {

  transform(value: any, args?: any): any {
    if (value === undefined || value === null) return '# value is undefined or null #';  // (3)
    if (args === undefined || args === null) return '# args is undefined or null #'  // (4)
    let s = moment(value).format(args);
    return s;
  }

}
No 解説
(1) moment を参照します
(2) name に指定した名前で Pipe を利用できます
(3) 日付 or 日付形式の文字列が定義されているかのチェックです
(4) 引数 (フォーマット文字列) が指定されているかのチェックです

3. Module 登録

Pipe を実装したら、忘れずに Module に登録します。

angular-cli を使って生成した場合は追加されているかも。

以下は、Pipe をまとめた PipesModule を定義した場合の例です。

// import 省略

@NgModule({
  imports: [
    CommonModule
  ],
  exports: [
    MomentPipe  // (1)
  ],
  declarations: [
    MomentPipe  // (2)
  ]
})
export class PipeModule { }
No 解説
(1) PipeModule を import したモジュールで MomentPipe を利用したい場合、exports に追加する必要があります。
(2) declarations に MomemtPipe を追加し、モジュール内で利用できるようにします。

まとめ

IE11 等で DatePipe でフォーマット指定がうまくいかないため、代替案として moment を利用した Pipe を作成しました。

DatePipe で困っている方は参考にしてみてください。