ネットでExpressionChangedAfterItHasBeenCheckedError
を検索すると、よく「子が親の状態を変更するとビューに一貫性がなくなることが警告される」という説明を目にしますが、少し混乱を招きかねない説明なんじゃないかなあと思いました。
正確には「Angularが変更検知をする際に行う探索順において、後からチェックされるビューから、先にチェックされるビューの状態を変える処理を書くと警告される」と説明すべきなのではないでしょうか。
なぜなら、HTMLには親子関係だけではなく、隣接(兄弟)関係というものがあるからです。親が子より先に探索されるのは当たり前ですが、隣接関係については自明ではありません。
例えば次のようなツリーがあったとします。
A, B | C
AとBは隣接(sibling)関係で、Cの親はAです。このときCの親であるAがCより先に探索されることは自明ですが、Bはどうでしょうか。Cより先でしょうか、後でしょうか。
もしAngularの変更検知が「幅優先探索」の順で行われるなら、Bが先です。しかし実際のAngularの変更検知順は「深さ優先探索」なので、検知される順に番号をふると、次のようになります。
A(1), B(3) | C(2)
つまりBは、Cよりも「後に」変更検知されるわけです。
だから探索順に置いて最後になるBにおいて、Cが依存するような状態を変更すると(先に確定されたCの状態を後からBが変えることになるので)ExpressionChangedAfterItHasBeenCheckedError
というやつが出ます(ただし警告してくれるのは開発ビルドのときだけ)。
よくウェブで見る「親と子」という説明の仕方だと、幅優先か深さ優先かで受け取る人の解釈が曖昧になるので「探索順」を使った説明をしたほうが親切なのではないでしょうか。