読者です 読者をやめる 読者になる 読者になる

画面に表示した項目が再描画の際に勝手に並び変わる

[事象]
初期描画で表示する時には想定した通りコンポーネントが並んで表示されるのに、
再描画以降では何故かコンポーネントが意図しない並び順序で表示されてしまい画面が崩れてしまう。

[原因]
並び替えが発生している箇所を調査すると、JSFやPrimeFacesのタグをそのまま利用している箇所ではなく、
自分でカスタムタグにて描画を行っている箇所であった。
当該カスタムタグでは複数のコンポーネントを生成していた。

調査した結果、どうもidではなくclientid順に並び替えが行われていた。
また、具合の悪い事に当該カスタムタグではclientidがnullになる様な仕掛けを実装してしまっており、
その結果意図しない順序に並び替えが発生していた。
なお、並び替えはDefaultFaceletsStateManagementStrategyクラス内でリストアなどを行う際に
Mapとかでイテレートを行っており、そのキーとしてclientidを用いているために
この様な挙動になってしまっているのかと推測しているが正しく追えていないので事実関係は不明である。

[対応方法]
最善策はカスタムタグを用いても正しく初期描画時に描画した内容で表示できる様に制御する事であるが、
残念ながらその方法について解析する事ができなかった。
次にとった方法としては、clientidを制御するという方法であるが、
clientidのフィールドを保持しているUIComponentBaseにはゲッターこそ用意されているが、
セッターは開放されていない。
また、基本的にはidが設定されていたらclientidをそこから導出する様な仕組みとなっている。
そのため、リフレクションとかで無理やり書き換えるという手段も最悪なくはないが、一旦諦める。

その為、最後にとった手段としては、コンポーネントツリーに対して無理やり階層を持たせる事で解決した。
自作したカスタムタグではラベルと入力系のコンポーネントを生成してそれを同階層のツリーに並べていたが、
そこにわざと階層を一つ加える事で並び替えが止まってくれた。
階層を持たす際に一番手っ取り早いのがdivタグなどを用いて階層を持たせれば良いのだが、
それでは残念ながら今回のレイアウトでは崩れが発生してしまった。
そこで、ui:fragmentタグを用いる事でこれを実現した。
ui:fragmentタグはそもそも判定条件をrenderer属性に埋め込む事で配下のツリーを表示・非表示制御するために用いるタグである。
この際、コンポーネントツリー上には当コンポーネントは生成されるが、
実際のHTMLには当コンポーネントを用いても一切影響を与えない。
この性質を利用して本来不用ではあるが、当タグを追加する事で並び替えを抑える事に成功した。

[その他]
但し、この方法では無駄にコンポーネントツリーを深くしてしまうので、
あまり多用しすぎると性能劣化に少なからず影響を与えてしまう。
また、そもそも本質的な解法ではないので、きちんとした理由を抑えて、それに対する対策をうつのが正攻法なのはいうまでもない。

わかりやすいJavaEEウェブシステム入門

わかりやすいJavaEEウェブシステム入門

Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築

Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築

Beginning Java EE 7 (Expert Voice in Java)

Beginning Java EE 7 (Expert Voice in Java)