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.

Другие вопросы по тегам