Pregunta

Estoy teniendo un problema.

He implementado un PhaseListener, que está destinado a añadir una clase de estilo a cualquier componente UIInput en el árbol que tiene mensajes adjuntos a ellos, y elimina la clase de estilo si no tiene ningún mensajes adjuntos a ellos.

El PhaseListener se ejecuta en la fase RENDER_RESPONSE, y no es un trabajo, tanto en los métodos y beforePhase afterPhase durante la depuración. Durante la depuración, me encontré con que beforePhase no tiene acceso al árbol de componentes completo, pero afterPhase hace. Cualquier cambio hecho en afterPhase no se representan sin embargo.

¿Cómo hago para esto? Quiero que esto sea completamente del lado del servidor.

Gracias,

James

¿Fue útil?

Solución 2

implementa utilizando un ViewHandler, sin embargo no es eficiente. PhaseListener en Render fase de respuesta no tiene acceso al árbol de componentes.

Otros consejos

El árbol de componentes JSF sólo está disponible después de que el tiempo de visualización de construcción. La fase RENDER_RESPONSE no es necesariamente un buen momento para tener acceso al árbol de componentes JSF completo antes de que se dicte. Durante una solicitud GET inicial sin ningún <f:viewAction>, el árbol de componentes completo sólo está disponible en el afterPhase como está siendo construido durante el RENDER_RESPONSE. Durante una devolución de datos del árbol de componentes completo está disponible en el beforePhase, sin embargo, cuando una navegación a un punto de vista diferente ha tenido lugar, entonces sería Stil ser cambiado durante la fase RENDER_RESPONSE, por lo que cualquier modificación se perdiera .

Para saber cuál es el tiempo de construcción es vista, la cabeza a la pregunta ¿Cuál es el tiempo de visualización de construcción ?

Es, básicamente, quiere enganchar "privar de tiempo" en lugar de beforePhase de la fase RENDER_RESPONSE. JSF ofrece varias formas de conectar en él:

  1. En algunos plantilla maestra, adjuntar un oyente preRenderView a <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. O, implementar un SystemEventListener para 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.
            // ...
        }
    
    }
    

    Para conseguir que se ejecute, se registra como abajo en 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. O, proporcionar una costumbre ViewHandler que hacer el trabajo en el 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;
        }
    
    }
    

    Para conseguir que se ejecute, se registra como a continuación en faces-config.xml:

    <application>
        <view-handler>com.example.YourViewHandler</view-handler>
    </application>
    
  4. O, gancho en ViewDeclarationLanguage#renderView() , pero esto es un poco en el borde, ya que no es realmente intented para manipular el árbol de componentes, sino para manipular el modo de hacer la vista.


Sin relación para el problema concreto, todo esto no es la solución adecuada para el requisito funcional concreta como se indica en su pregunta:

  

que está destinado a añadir una clase de estilo a cualquier componente UIInput en el árbol que tiene mensajes adjuntos a ellos, y elimina la clase de estilo si no tiene ningún mensajes adjuntos a ellos

Usted sería realmente mejor cabeza para una solución del lado del cliente en lugar de manipular el árbol de componentes (que terminaría en el estado de componentes JSF!). Imagínese el caso de entradas en los componentes de iteración como <ui:repeat><h:inputText>. Hay físicamente único componente de una entrada en el árbol, que no sea múltiplo! La manipulación de la clase de estilo a través de UIInput#setStyleClass() conseguiría presentado en cada ronda de iteración.

Será mejor que visitar el árbol de componentes utilizando UIViewRoot#visitTree() que a continuación y recoger todos los ID de cliente de componentes de entrada no válidos (este enfoque visitTree() transparentemente tomar en cuenta los componentes de iteración):

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;
    }
});

Y a continuación, pasar a partir de entonces en invalidInputClientIds sabor de una matriz JSON tener JavaScript que luego apoderarse de ellos a través de document.getElementById() y alterar el atributo className.

for (var i = 0; i < invalidInputClientIds.length; i++) {
    var invalidInput = document.getElementById(invalidInputClientIds[i]);
    invalidInput.className += ' error';
}

El JSF utilidad biblioteca OmniFaces tiene un componente <o:highlight> que hace exactamente esto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top