1ってつけたけど、2があるかはわかりません。
事件の概要
カスタムのセレクトコンポーネントにキーバインドを実装した際、↑↓キーを使うと現在選択されている選択肢につけている selecting
というクラス名が消失する事件が起きました。キーイベントの処理では selecting
クラスつけ外しの基準となる変数には一切触れていないのに、一体なぜでしょうか
事件の現場
謎解きをしたい人向けに簡単に再現をStackBlitzで作って見ました。再現用にいらない部分を出来るだけ削ったのでコンポーネントとしては色々アレですが、謎解きには十分だと思います。事件発生の経緯は以下の通りです。
- inputをクリックするとオプションが表示されます
- 現在valueとして選択されている値には
selected
クラスが付き、水色でハイライトされます。 - オプション表示時に↑↓キーを押すと、フォーカスされている選択肢が薄いグレー混じりの水色でハイライトされます。が、これをすると現在valueとして選択している値自体には変更がないのに
selected
クラスが消え、水色のハイライトが無くなります。
調査結果
調査を進めると、「とりあえず[class.XXX]
で記述していたselectingクラスの制御を{{ XXX }}
で書き換えると事件は起きなくなる」ということがわかりました。実際に、現場StackBlitzでそのように書き換えると起きなくなることが確認できます。(その逆で、focusのクラス指定をクラスバインディングにしてもちゃんと動きます。)
しかしながら、回避策はわかったものの原因が全くわからないので調査を続けることにします。普通に検索しようとしても、なかなか求めている答えにはぶつかりません。そこで、直接angularのレポジトリのissueで「interpolation class」で狙い打ちしてみることにしました。そしてついに答えを見つけることができたのです。
真相
クラス名が消えてしまうのは、「interpolationによるクラス名の付与はクラスリスト全体をバインドするため、ChangeDetectionが走ってクラスリストが再設定された時に、プログラムから付与したクラス名が消える」からでした。
実際にその部分だけ試してみたいという人のために、ミニマルStackBlitzをつくりました。IMPORTANTを押してからCOLORを押すと、IMPORTANTにより付与されたクラス(およびそのクラスに付随するスタイル)が消えることがわかります。
結論
クラスバインディング [class.XXX]
とinterpolation {{ XXX }}
でのクラス名の付与は併用しないのが一番。どちらかにしましょう
一般的にはミニマルレポのように、変数名をそのままクラスに使いたい時がinterpolationの出番で、クラスバインディングとは微妙に使いたい時が違うと思うので混ぜたい時が少なからずあるとは思います。ただ、ChangeDetectionを完全把握して影響のないように設定できるなら話は別ですが、人間の頭では難しい気がしています。
関係ないですが、私はフォーマットかけた時になんとなく見やすいのでクラスバインディングに揃える方が好きです。