2010-03-18 2 views
1

У меня проблема.Изменение дерева компонентов JSF в PhaseListener

Я реализовал PhaseListener, который предназначен для добавления класса стиля к любым компонентам UIInput в дереве с прикрепленными к ним сообщениями и удаляет класс стиля, если у него нет сообщений, прикрепленных к ним.

PhaseListener работает в фазе RENDER_RESPONSE и работает ли он как в методах beforePhase, так и afterPhase во время отладки. Во время отладки я обнаружил, что beforePhase не имеет доступа к полному дереву компонентов, но afterPhase делает. Любые изменения, сделанные в afterPhase, пока не отображаются.

Как это сделать? Я хочу, чтобы это была полностью серверная.

Спасибо,

Джеймс

ответ

0

Реализовано с использованием, однако обработки просмотров это не эффективно. PhaseListener в фазе Render Response не имеет доступа к дереву компонентов.

2

Дерево компонентов JSF доступно только после времени сборки представления. Фаза RENDER_RESPONSE не обязательно является хорошим моментом для доступа к полному дереву компонентов JSF до того, как он будет обработан. Во время первоначального запроса GET без <f:viewAction>, полное дерево компонентов доступно только в afterPhase, поскольку оно строится во время RENDER_RESPONSE. Во время обратной передачи полное дерево компонентов доступно в beforePhase, однако при переходе на другое представление было бы изменено во время фазы RENDER_RESPONSE, поэтому любые модификации будут потеряны.

Чтобы узнать, что именно в то время вид сборки, голова на вопрос What's the view build time?

Вы в основном хотите зацепить «зрения времени визуализации», а не beforePhase из RENDER_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(), но это немного на краю, как это на самом деле не intented манипулировать дерево компонентов, но для управления визуализацией представления.


Unrelated к конкретной проблеме, все это не является правильным решением для конкретного функционального требования, как указано в вашем вопросе:

, который предназначен, чтобы добавить стиль класс к любым компонентам UIInput в дереве с прикрепленными к ним сообщениями и удаляет класс стиля, если у него нет сообщений, прикрепленных к ним.

Вам лучше подойдет для решения на стороне клиента, а не для управления деревом компонентов (что закончится в состоянии компонента JSF!). Представьте пример входов в итерационные компоненты, такие как <ui:repeat><h:inputText>. Физически только один компонент ввода в дереве, а не несколько! Манипуляция классом стиля через UIInput#setStyleClass() будет представлена ​​в каждом раунде итераций.

Вы бы лучше посетить дерево компонентов с использованием UIViewRoot#visitTree(), как показано ниже, и собрать все клиентские идентификаторы недопустимых входных компонентов (это 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 OmniFaces имеет <o:highlight> компонент, который делает именно это.

+0

спасибо за это. Я на самом деле не коснулся JSF годами :), но, возможно, это полезно для других. – jamiebarrow

Смежные вопросы