Pergunta

Estou tendo um problema.

Eu implementei um Phaselistener, que visa adicionar uma classe de estilo a qualquer componente de UIInput na árvore que tenha mensagens anexadas a eles e remove a classe de estilo se não tiver nenhuma mensagem anexada a eles.

O Phaselistener é executado na fase Render_Response e faz o trabalho nos métodos antes da fase e da fase pós -fase durante a depuração. Durante a depuração, descobri que a fase anterior não tem acesso à árvore completa dos componentes, mas a fase após. Quaisquer mudanças feitas na fase pós -fase não são renderizadas.

Como faço para fazer isso? Eu quero que este seja um lado completamente do servidor.

Obrigado,

James

Foi útil?

Solução 2

Implementado usando um viewhandler, no entanto, não é eficiente. Phaselistener na fase de resposta de renderização não tem acesso à árvore componente.

Outras dicas

A árvore de componentes JSF está disponível apenas após o tempo de construção. o RENDER_RESPONSE A fase não é necessariamente um bom momento para ter acesso à árvore completa do componente JSF antes de ser renderizada. Durante uma solicitação inicial inicial sem nenhum <f:viewAction>, a árvore de componentes completos está disponível apenas no afterPhase como está sendo construído durante o RENDER_RESPONSE. Durante um postback, a árvore de componentes completa está disponível no beforePhase, no entanto, quando uma navegação para uma visão diferente ocorreu, então ela seria alterada durante a RENDER_RESPONSE fase, então quaisquer modificações se perderiam.

Para saber o que exatamente é o tempo de construção da visão, vá para a pergunta Qual é o tempo de criação de criação?

Você basicamente quer se conectar em "Visualizar tempo de renderizar" em vez de beforePhase do RENDER_RESPONSE Estágio. A JSF oferece várias maneiras de prender:

  1. Em algum modelo mestre, anexe um preRenderView ouvinte para <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. Ou implementar um global SystemEventListener por 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 executá -lo, registre -o como abaixo em 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. Ou forneça um costume ViewHandler onde você faz o trabalho 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 executá -lo, registre -se como abaixo em faces-config.xml:

    <application>
        <view-handler>com.example.YourViewHandler</view-handler>
    </application>
    
  4. Ou, enganchar ViewDeclarationLanguage#renderView(), mas isso está um pouco no limite, pois não é realmente pretendido manipular a árvore dos componentes, mas manipular como renderizar a vista.


Não relacionado Para o problema concreto, tudo isso não é a solução certa para o requisito funcional concreto, conforme declarado em sua pergunta:

que visa adicionar uma classe de estilo a qualquer componente da UIInput na árvore que tenha mensagens anexadas a eles e remove a classe de estilo se não tiver nenhuma mensagem anexada a eles

Você realmente seria melhor para uma solução do lado do cliente, em vez de manipular a árvore de componentes (que acabaria no estado do componente JSF!). Imagine o caso de entradas em componentes de iteração, como <ui:repeat><h:inputText>. Existe fisicamente apenas um componente de entrada na árvore, não múltiplo! Manipulando a classe de estilo via UIInput#setStyleClass() seria apresentado em todas as rodadas de iteração.

É melhor você visitar a árvore de componentes usando UIViewRoot#visitTree() como abaixo e coletar todos os IDs de clientes de componentes de entrada inválidos (este visitTree() A abordagem levará em consideração transparentemente os componentes de iteração):

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 depois depois invalidInputClientIds em sabor de uma matriz JSON para JavaScript que os agarrará via document.getElementById() e alterar o className atributo.

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

A Biblioteca de Utilitário JSF Omnifaces tem um <o:highlight> componente que faz exatamente isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top