質問

問題があります。

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 フェーズなので、変更は失われます。

ビューのビルド時間の正確な時間を知りたい場合は、質問に進んでください。 ビューの構築時間はどれくらいですか?

基本的には、「ビューのレンダリング時間」にフックしたいのです。 beforePhaseRENDER_RESPONSE 段階。JSF には、これをフックするいくつかの方法が用意されています。

  1. 一部のマスター テンプレートに、 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.
        // ...
    }        
    
  2. または、グローバルを実装します。 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>
    
  3. または、カスタムを提供します 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>
    
  4. または、フックオン 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> まさにこれを行うコンポーネントです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top