AngularでDateオブジェクトを変更検知するときは新しいDateオブジェクトを使う

こんにちは。thiroyoshiです。

AngularでDateを使っていて、変更検知のところで一瞬ハマったので、そのメモです。

課題:Date.setDate()で画面の日付が変わってくれない

こんなコードを書きました。コード中のselectedDateは、画面上で選択中の日付を入れています。

backDate() { 
  const date = new Date(this.selectedDate)
  this.selectedDate.setDate(date.getDate() - 1);
}

これをbutton要素にクリックイベントとして紐づけます。

selectedDateの変更検知をトリガーにして、表示上の日付を変更しつつ、その日付をもとにAPIを実行する、というのをしたかったのです。

これでは画面上の日付の表示は変わりません。しかし、ログを出すと意図通りに変更されていたのです。

つまり、selectedDateが変更検知されなかったのです。

解決策:this.selectedDateに新たなDateオブジェクトを入れる

結局、以下のように新たなDateオブジェクトを作って代入する形にしました。

backDate() { 
  const date = new Date(this.selectedDate)
  date.setDate(date.getDate() - 1);
  this.selectedDate = date;
}


結果的に、date.setDate()では変更検知せず、this.selectedDate = dateという形で代入していかないと変更検知してくれなかったのです。

Dateの場合には、new Date(this.selectedDate)って形で別にDateオブジェクトを作って、それを代入する形を取る必要があるんですね。

変更検知にはオブジェクトそのものを変更する必要がある

Angularの変更検知の実態は、NgZoneというもので、Objectの変更検知をしています。

詳しい仕様は見ていませんが、今回はNumberやStringではなく、クラスオブジェクトの中をいじる、つまるクラスの内部状態だけを変更する形だったので、これは変更検知されなかったのでしょう。

もうちょっとスマートな方法もあるかもしれないですが、やりたいことは単純に現在日付を変更したいだけなのでこれで十分です。個別のブラウザ上での処理なので、気にするほど速度も遅くならないでしょうし。