ui: повторное выражение значения, вычисляемое при каждом запросе ajax
У меня есть страница с очень простой формой, которая отправляет запросы ajax, предназначенные только для компонентов в той же форме. На той же странице (но за пределами формы) есть также ui:repeat
который выполняет итерацию по массиву, возвращенному из управляемого объекта EJB (предположим, список категорий продуктов). Этот bean-компонент не имеет свойств, связанных в форме, и к нему нельзя получить доступ любым другим способом, кроме value
атрибут ui:repeat
тег. Я не понимаю, почему JSF должен воссоздавать bean-объект с областью запроса при каждой обратной передаче ajax, как если бы я просил визуализировать этот внешний ui:repeat
(который не имеет ничего общего с формой) вместе с некоторым компонентом внутри формы.
Это ошибка? Или это ожидаемое поведение? Конечно, я мог бы аннотировать боб как ViewScoped
но я не вижу причины хранить категории в области просмотра, поскольку они полностью статичны между постбеками.
Другое решение / обходной путь, который я нашел, это сделать ui:repeat
только в случае запросов без AJAX:
<ul>
<ui:repeat value="#{someRequestScopedBean.categories}" var="category" rendered="#{not facesContext.partialViewContext.ajaxRequest}">
<li>#{category.name}</li>
</ui:repeat>
</ul>
но я не знаю, может ли это вызвать проблемы и выглядит не очень ясно.
ПРЕЦЕДЕНТ
index.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h1>Test page</h1>
<p>Random jokes</p>
<ul>
<ui:repeat value="#{oneLiners.list}" var="oneliner">
<li>#{oneliner}</li>
</ui:repeat>
</ul>
<h:form>
<h:selectOneMenu value="#{backingBean.greeting}" hideNoSelectionOption="true">
<f:selectItem value="#{null}" itemLabel="Select a greeting..." noSelectionOption="true"/>
<f:selectItems value="#{backingBean.greetings}"/>
<f:ajax render="@this btn"/>
</h:selectOneMenu>
<h:commandButton id="btn" value="Say Hello!" disabled="#{empty backingBean.greeting}">
<f:ajax render="otxt"/>
</h:commandButton>
<h:outputText id="otxt" value="#{backingBean.greeting}, Maurizio!" style="display: #{empty backingBean.greeting ? 'none' : 'block'}"/>
</h:form>
</h:body>
</html>
Запросить bean-объект:
package testuirepajax;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
/**
*
* @author maurizio
*/
@ManagedBean
@RequestScoped
public class OneLiners {
private String[] list;
public OneLiners() {
System.out.println("testuirepajax.OneLiners.<init>()");
list = new String[] {
"Life is wonderful. Without it we'd all be dead.",
"Daddy, why doesn't this magnet pick up this floppy disk?",
"Daddy, what does FORMATTING DRIVE C mean?",
"Never forget: 2 + 2 = 5 for extremely large values of 2.",
"C:\\ is the root of all directories."
};
}
public String[] getList() {
System.out.println("testuirepajax.OneLiners.getList()");
return list;
}
}
Форма основы бобов:
package testuirepajax;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
/**
*
* @author maurizio
*/
@ManagedBean
@ViewScoped
public class BackingBean {
private String[] greetings;
private String greeting;
public BackingBean() {
System.out.println("testuirepajax.BackingBean.<init>()");
greetings = new String[] {
"Hello", "Hi", "Good morning", "Good evening", "Good night"
};
}
public String[] getGreetings() {
return greetings;
}
public void setGreeting(String greeting) {
this.greeting = greeting;
}
public String getGreeting() {
return greeting;
}
}
Проверьте вывод вашего контейнера. Используя сервер Payara (который поставляется с Mojarra 2.2.12), я вижу такие строки:
Informazioni: testuirepajax.OneLiners.<init>()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.<init>()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
Informazioni: testuirepajax.OneLiners.getList()
при выборе элементов из меню или нажатии "Скажи привет!" кнопка.
1 ответ
Я поставил точку останова на getList()
Метод и проверил стек вызовов, когда он "излишне" ударил во время обратной передачи, чтобы узнать, кто и почему:
Daemon Thread [http-nio-8088-exec-5] (Suspended (breakpoint at line 23 in OneLiners))
owns: NioChannel (id=83)
OneLiners.getList() line: 23
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 497
BeanELResolver.getValue(ELContext, Object, Object) line: 97
DemuxCompositeELResolver._getValue(int, ELResolver[], ELContext, Object, Object) line: 176
DemuxCompositeELResolver.getValue(ELContext, Object, Object) line: 203
AstValue.getValue(EvaluationContext) line: 169
ValueExpressionImpl.getValue(ELContext) line: 184
TagValueExpression.getValue(ELContext) line: 109
UIRepeat.getValue() line: 279
UIRepeat.getDataModel() line: 255
UIRepeat.visitTree(VisitContext, VisitCallback) line: 727
HtmlBody(UIComponent).visitTree(VisitContext, VisitCallback) line: 1700
UIViewRoot(UIComponent).visitTree(VisitContext, VisitCallback) line: 1700
PartialViewContextImpl.processComponents(UIComponent, PhaseId, Collection<String>, FacesContext) line: 403
PartialViewContextImpl.processPartial(PhaseId) line: 266
UIViewRoot.processDecodes(FacesContext) line: 927
ApplyRequestValuesPhase.execute(FacesContext) line: 78
ApplyRequestValuesPhase(Phase).doPhase(FacesContext, Lifecycle, ListIterator<PhaseListener>) line: 101
LifecycleImpl.execute(FacesContext) line: 198
FacesServlet.service(ServletRequest, ServletResponse) line: 658
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 291
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
WsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 52
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 239
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
StandardWrapperValve.invoke(Request, Response) line: 212
StandardContextValve.invoke(Request, Response) line: 106
FormAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 502
StandardHostValve.invoke(Request, Response) line: 141
ErrorReportValve.invoke(Request, Response) line: 79
AccessLogValve(AbstractAccessLogValve).invoke(Request, Response) line: 616
StandardEngineValve.invoke(Request, Response) line: 88
CoyoteAdapter.service(Request, Response) line: 521
Http11NioProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1096
Http11NioProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 674
NioEndpoint$SocketProcessor.doRun() line: 1500
NioEndpoint$SocketProcessor.run() line: 1456
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617
TaskThread$WrappingRunnable.run() line: 61
TaskThread(Thread).run() line: 745
Интересные строки - те, что выше FacesServlet
, Имена классов / методов уже говорят сами за себя.
Таким образом, это произошло на этапе применения значений запроса, когда частичному запросу необходимо обработать декодирование компонентов. Дерево компонентов посещается, чтобы найти компоненты, идентифицированные идентификаторами клиента, указанными в <f:ajax execute>
(который по умолчанию @this
). Как <ui:repeat>
находится в пути перед интересующим компонентом, сначала проверяется. visitTree()
запускает полную итерацию, потому что интересующие идентификаторы клиента доступны только во время итерации.
Когда я переехал <ui:repeat>
ниже <h:form>
больше не вызывается. Все компоненты, представляющие интерес, уже были найдены в этот момент.
Такое поведение, к сожалению, "по замыслу". Ваша работа хороша. Лучше это проверить #{not facesContext.postback}
вместо этого, поскольку это также охватывает постбэки не-ajax.