Domanda

Sto avendo un problema.

Ho implementato un PhaseListener, che ha lo scopo di aggiungere una classe di stile a qualsiasi componente UIInput nella struttura che hanno i messaggi ad essi, ed elimina la classe di stile se non ha alcun messaggio ad essi connessi.

Il PhaseListener viene eseguito in fase di RENDER_RESPONSE, e non è un lavoro in entrambi i metodi beforePhase e afterPhase durante il debug. Durante il debug, ho scoperto che beforePhase non ha accesso alla struttura ad albero pieno componente, ma afterPhase fa. Eventuali cambiamenti fatti in afterPhase non sono resi però.

Come posso fare questo? Voglio che questo sia completamente lato server.

Grazie,

James

È stato utile?

Soluzione 2

implementato usando un ViewHandler, tuttavia non è efficiente. PhaseListener in Render fase di risposta non ha accesso alla struttura ad albero dei componenti.

Altri suggerimenti

L'albero dei componenti JSF è disponibile solo dopo il tempo di visualizzazione di compilazione. La fase RENDER_RESPONSE non è necessariamente un buon momento per avere accesso alla piena albero componente JSF prima che venga eseguito il rendering. Durante una richiesta GET iniziale senza <f:viewAction>, l'albero pieno componente è disponibile solo nella afterPhase mentre viene costruita durante il RENDER_RESPONSE. Nel corso di un postback l'albero pieno componente è disponibile nel beforePhase, tuttavia, quando una navigazione ad una visione diversa ha avuto luogo, allora sarebbe stil essere cambiato durante il la fase RENDER_RESPONSE, quindi eventuali modifiche si perderebbe .

Per sapere che cosa esattamente il tempo di visualizzazione di compilazione è, la testa alla domanda Che ore sono vista accumulo ?

È fondamentalmente vuole agganciare su "visualizza il tempo di rendering", piuttosto che beforePhase della fase RENDER_RESPONSE. JSF offre diversi modi per agganciare su di esso:

  1. In un certo modello principale, collegare un ascoltatore 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. In alternativa, implementare un SystemEventListener per 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.
            // ...
        }
    
    }
    

    Per farlo funzionare, registrarsi come qui sotto in 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. In alternativa, fornire una consuetudine ViewHandler in cui si fa il lavoro in 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;
        }
    
    }
    

    Per farlo funzionare, registrare, come di seguito in faces-config.xml:

    <application>
        <view-handler>com.example.YourViewHandler</view-handler>
    </application>
    
  4. In alternativa, gancio sulla ViewDeclarationLanguage#renderView() , ma questo è un po 'al limite in quanto non è davvero intented di manipolare l'albero componente, ma di manipolare come rendere la vista.


non collegati per il problema concreto, tutto questo non è la soluzione giusta per il requisito funzionale concreta che appare nella tua domanda:

  

che si propone di aggiungere una classe di stile a qualsiasi componente UIInput nella struttura che hanno i messaggi ad essi, ed elimina la classe di stile se non ha alcun messaggio ad essi

È piacerebbe molto meglio la testa per una soluzione lato client piuttosto che manipolare l'albero componente (che finirebbe in stato componente JSF!). Immaginare il caso di ingressi in componenti iterazione come <ui:repeat><h:inputText>. C'è fisicamente unico componente un ingresso nella struttura, non di più! Manipolazione della classe di stile tramite UIInput#setStyleClass() otterrebbe presentato in ogni iterazione di turno.

Faresti meglio visitare l'albero componente utilizzando UIViewRoot#visitTree() come qui sotto e raccogliere tutti gli ID cliente di componenti di ingresso non validi (questo approccio visitTree() in modo trasparente prendere componenti iterazione in considerazione):

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

E poi successivamente passare invalidInputClientIds nel sapore di una matrice JSON a JavaScript che poi li afferra tramite document.getElementById() e modificare l'attributo className.

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

Il JSF utility biblioteca OmniFaces ha un componente <o:highlight> che fa esattamente questo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top