2016-02-22 29 views
1

我有一個非常簡單的表單,它只提交針對同一表單中的組件的ajax請求。在同一頁面(但在表單外),還有一個ui:repeat,它對從請求作用域託管bean(假設產品類別列表)返回的數組進行迭代。該bean沒有綁定在表單中的屬性,除了ui:repeat標記的value屬性之外,沒有以任何其他方式進行訪問。我不明白爲什麼JSF需要在每個ajax回發中重新創建請求範圍的bean,只需就好像我要求將此外部ui:repeat(與表單無關)與表單中的某個組件一起呈現。ui:在每個ajax請求上評估的重複值表達式

這是一個錯誤?或者它是一種預期的行爲?當然,我可以將bean註釋爲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; 
    } 
} 

形式支持bean:

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; 
    } 
} 

檢查您的容器的輸出。使用似鯖水狼牙魚服務器(船鑽嘴魚科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() 

從菜單中選擇元素或單擊時「打招呼!」按鈕。

+0

它是否包含輸入組件?哪個JSF impl /版本?類似的錯誤在Mojarra 2.2.7和2.1.29中得到修復。有很好的解決方法!如果有必要的話,可以在未來的讀者加入之前添加'<! - comment - >'。 – BalusC

+0

@BalusC 1)表單中包含一對'h:selectOneMenu'和一個'h:button',但是也出現在包含'h:commandButton'的表單中。 2)Mojarra 2.2.12 – maurizeio

+0

他們在ui裏面:重複?那麼,這種副作用是不可避免的,因爲無論如何JSF需要重申它以便在回發期間恢復這些組件的狀態。 「部分恢復」不幸的是不支持JSF(雞蛋和所有)。 – BalusC

回答

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)中指定的客戶端ID標識的組件。由於<ui:repeat>在感興趣的組件之前,它首先被檢查。 A visitTree()會觸發完整迭代,因爲感興趣的客戶端ID僅在迭代過程中可用。

當我將<ui:repeat>移動到<h:form>以下時,它不再被調用。所有感興趣的組件都已經在那個時候被發現。

不幸的是,這種行爲是「按設計」。你的工作是一個很好的解決方案。更好的辦法是檢查#{not facesContext.postback},因爲這也包括非ajax回發。

+0

謝謝Balus。看看我製作的其他小平面,我也在想同樣的想法(移動'ui:repeat'以查看位置是否相關)。現在一切都清澈透明。 – maurizeio