PhaseListener での JSF コンポーネント ツリーの変更
質問
問題があります。
PhaseListener を実装しました。これは、メッセージが添付されているツリー内の UIInput コンポーネントにスタイル クラスを追加し、メッセージが添付されていない場合はスタイル クラスを削除することを目的としています。
PhaseListener は RENDER_RESPONSE フェーズで実行され、デバッグ中に beforePhase メソッドと afterPhase メソッドの両方で動作します。デバッグ中に、beforePhase は完全なコンポーネント ツリーにアクセスできないが、afterPhase はアクセスできることがわかりました。ただし、afterPhase で行われた変更はレンダリングされません。
これについてはどうすればよいでしょうか?これを完全にサーバー側にしたいのです。
ありがとう、
ジェームス
解決 2
のViewHandlerを使用して実装、しかし、それは効率的ではありません。 PhaseListenerでの応答位相はコンポーネントツリーにアクセスすることはできませんレンダリングします。
他のヒント
JSF コンポーネント ツリーは、ビューの構築後にのみ使用できます。の RENDER_RESPONSE
フェーズは、レンダリングされる前に完全な JSF コンポーネント ツリーにアクセスできる時期であるとは限りません。何も指定されていない最初の GET リクエスト中 <f:viewAction>
, 、完全なコンポーネント ツリーは、 afterPhase
期間中に構築されているため、 RENDER_RESPONSE
. 。ポストバック中に、完全なコンポーネント ツリーが次の場所で利用可能になります。 beforePhase
, ただし、別のビューへのナビゲーションが行われた場合でも、ビューは変更されます。 その間 の RENDER_RESPONSE
フェーズなので、変更は失われます。
ビューのビルド時間の正確な時間を知りたい場合は、質問に進んでください。 ビューの構築時間はどれくらいですか?
基本的には、「ビューのレンダリング時間」にフックしたいのです。 beforePhase
の RENDER_RESPONSE
段階。JSF には、これをフックするいくつかの方法が用意されています。
一部のマスター テンプレートに、
preRenderView
リスナーに<f:view>
.<f:view ...> <f:event type="preRenderView" listener="#{bean.onPreRenderView}" /> ... </f:view>
public void onPreRenderView(ComponentSystemEvent event) { UIViewRoot view = (UIViewRoot) event.getSource(); // The view is the component tree. Just modify it here accordingly. // ... }
または、グローバルを実装します。
SystemEventListener
のためにPreRenderViewEvent
.public class YourPreRenderViewListener implements SystemEventListener { @Override public boolean isListenerForSource(Object source) { return source instanceof UIViewRoot; } @Override public void processEvent(SystemEvent event) throws AbortProcessingException { UIViewRoot view = (UIViewRoot) event.getSource(); // The view is the component tree. Just modify it here accordingly. // ... } }
実行するには、以下のように登録します。
faces-config.xml
:<application> <system-event-listener> <system-event-listener-class>com.example.YourPreRenderViewListener</system-event-listener-class> <system-event-class>javax.faces.event.PreRenderViewEvent</system-event-class> </system-event-listener> </application>
または、カスタムを提供します
ViewHandler
あなたがその仕事をする場所renderView()
.public class YourViewHandler extends ViewHandlerWrapper { private ViewHandler wrapped; public YourViewHandler(ViewHandler wrapped) { this.wrapped = wrapped; } @Override public void renderView(FacesContext context, UIViewRoot view) { // The view is the component tree. Just modify it here accordingly. // ... // Finally call super so JSF can do the rendering job. super.renderView(context, view); } @Override public ViewHandler getWrapped() { return wrapped; } }
実行するには、以下のように登録します。
faces-config.xml
:<application> <view-handler>com.example.YourViewHandler</view-handler> </application>
または、フックオン
ViewDeclarationLanguage#renderView()
, 、しかし、これは実際にはコンポーネントツリーを操作することを目的としたものではなく、ビューのレンダリング方法を操作することを目的としているため、少し限界があります。
無関係 具体的な問題に関して言えば、これはすべて、質問に記載されている具体的な機能要件に対する正しい解決策ではありません。
これは、メッセージが添付されているツリー内の任意の UIInput コンポーネントにスタイル クラスを追加し、メッセージが添付されていない場合はスタイル クラスを削除することを目的としています。
コンポーネント ツリーを操作する (最終的には JSF コンポーネントの状態になってしまいます) よりも、クライアント側のソリューションを使用することをお勧めします。次のような反復コンポーネントの入力の場合を想像してください。 <ui:repeat><h:inputText>
. 。ツリー内に物理的に存在する入力コンポーネントは 1 つだけであり、複数ではありません。スタイルクラスを操作する UIInput#setStyleClass()
すべての反復ラウンドで提示されます。
次を使用してコンポーネント ツリーにアクセスすることをお勧めします。 UIViewRoot#visitTree()
以下のように、無効な入力コンポーネントのすべてのクライアント ID を収集します (これは visitTree()
このアプローチでは反復コンポーネントが透過的に考慮されます):
Set<String> invalidInputClientIds = new HashSet<>();
view.visitTree(VisitContext.createVisitContext(context, null, EnumSet.of(VisitHint.SKIP_UNRENDERED)), new VisitCallback() {
@Override
public VisitResult visit(VisitContext context, UIComponent component) {
if (component instanceof UIInput) {
UIInput input = (UIInput) component;
if (!input.isValid()) {
invalidInputClientIds.add(input.getClientId(context.getFacesContext()));
}
}
return VisitResult.ACCEPT;
}
});
そしてその後通過します invalidInputClientIds
JSON 配列のフレーバーを JavaScript に変換し、それを介してそれらを取得します。 document.getElementById()
そして、 className
属性。
for (var i = 0; i < invalidInputClientIds.length; i++) {
var invalidInput = document.getElementById(invalidInputClientIds[i]);
invalidInput.className += ' error';
}
JSFユーティリティライブラリ オムニフェイス があります <o:highlight>
まさにこれを行うコンポーネントです。