Дерево компонентов JSF доступно только после времени сборки представления. Фаза RENDER_RESPONSE
не обязательно является подходящим моментом для доступа к полному дереву компонентов JSF до его рендеринга. Во время первоначального запроса GET без каких-либо <f:viewAction>
полное дерево компонентов доступно только в afterPhase
, так как оно строится во время RENDER_RESPONSE
. Во время обратной передачи полное дерево компонентов доступно на этапе beforePhase
, однако, когда происходит переход к другому представлению, оно все равно будет изменено во время этапа RENDER_RESPONSE
, поэтому любые изменения будут потеряны. .
Чтобы узнать точное время сборки представления, ответьте на вопрос Каково время сборки представления? а>
В основном вы хотите подключиться к «просмотру времени рендеринга», а не к beforePhase
из RENDER_RESPONSE
фазы. JSF предлагает несколько способов зацепиться за него:
В каком-нибудь мастер-шаблоне прикрепите прослушиватель 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.
// ...
}
Или реализуйте глобальный SystemEventListener
для < a href="http://docs.oracle.com/javaee/7/api/javax/faces/event/PreRenderViewEvent.html" rel="nofollow noreferrer">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>
Или укажите собственный 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>
Или подключитесь к ViewDeclarationLanguage#renderView()
, но это немного на грани, поскольку на самом деле он предназначен не для управления деревом компонентов, а для управления способом визуализации представления.
Не связанный с конкретной проблемой, все это не является правильным решением для конкретного функционального требования, как указано в вашем вопросе:
который предназначен для добавления класса стиля к любым компонентам 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>
, который делает именно это.
person
BalusC
schedule
10.11.2015