2015-07-11 14 views
2

裏面我有以下的複合組件(僅限相關的代碼):通F:屬性到f:AJAX監聽複合執行

<cc:interface> 
     <cc:attribute name="message" type="com.virtuafisica.business.problemsmgmt.entity.Message" required="true" /> 
     <cc:attribute name="notifiedListener" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)" /> 
    </cc:interface> 

    <cc:implementation> 
     <h:selectBooleanCheckbox value="#{cc.attrs.message.notified}"> 
      <f:attribute name="msg" value="#{cc.attrs.message}" /> 
      <c:if test="#{cc.getValueExpression('notifiedListener') != null}"> 
       <f:ajax event="change" listener="#{cc.attrs.notifiedListener}" /> 
      </c:if> 
     </h:selectBooleanCheckbox> 
    </cc:implementation> 

該複合材料是由一個一個facelet叫:

而且變化事件在後臺bean中像這樣處理:

public void changeNotified(AjaxBehaviorEvent event) { 
    Message message = (Message) event.getComponent().getAttributes().get("msg"); 
     if (message == null) { 
      logger.log(Level.FINEST, "Null message!"); 
      return; 
     } 
     .... 
    } 

問題是該消息是null在後臺bean,雖然event.getComponent()是正確的(是HTMLSelectBooleanCheckbox)。

爲什麼不設置屬性?

謝謝你們

回答

2

的屬性設置(可以通過確定getValueExpression("msg")沒有返回null確認),但屬性值只是評估null。延遲表達式正好在您想要獲取該值時進行評估。因此,#{cc.attrs.message}正好在您在ajax偵聽器方法中調用get("msg")時進行評估。但是,在這一點上,在EL範圍內的任何地方都不存在#{cc}

解決方法正是這個技術問題將手動將其放回到EL範圍(和後來的清理!)。

UIComponent component = event.getComponent(); 
UIComponent cc = UIComponent.getCompositeComponentParent(component); 
Map<String, Object> requestScope = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); 

try { 
    requestScope.put("cc", cc); 
    Message message = (Message) component.getAttributes().get("msg"); 
} 
finally { 
    requestScope.remove("cc"); 
} 

另一個解決方法是直接從組合中獲取它。

UIComponent cc = UIComponent.getCompositeComponentParent(event.getComponent()); 
Message message = (Message) cc.getAttributes().get("msg"); 

不管怎樣,這只是相當笨拙爲後盾豆不應該需要知道的AJAX方法由複合調用。簡而言之,你有一個設計問題。

考慮創建一個支持組件並從那裏委託ajax調用,以便您可以直接傳遞Message作爲方法參數。

@FacesComponent("yourCompositeName") 
public class YourComposite extends UINamingContainer { 

    public void notifiedListener(AjaxBehaviorEvent event) { 
     MethodExpression notifiedListener = (MethodExpression) getAttributes().get("notifiedListener"); 
     Message message = (Message) getAttributes().get("message"); 
     notifiedListener.invoke(getFacesContext().getELContext(), new Object[] { message }); 
    } 

} 

註冊它作爲<cc:interface componentType>,改變方法屬性取Message參數,並讓<f:ajax>呼叫上述背襯組件上的聽者的方法,這將反過來代表對方法屬性。

<cc:interface componentType="yourCompositeName"> 
    <cc:attribute name="message" type="com.virtuafisica.business.problemsmgmt.entity.Message" required="true" /> 
    <cc:attribute name="notifiedListener" method-signature="void listener(com.virtuafisica.business.problemsmgmt.entity.Message)" /> 
</cc:interface> 

<cc:implementation> 
    <h:selectBooleanCheckbox value="#{cc.attrs.message.notified}"> 
     <c:if test="#{cc.getValueExpression('notifiedListener') != null}"> 
      <f:ajax listener="#{cc.notifiedListener}" /> 
     </c:if> 
    </h:selectBooleanCheckbox> 
</cc:implementation> 

(注意:我刪除event="change"因爲這是錯誤的一個複選框/單選按鈕,默認值是valueChange已經是正確的—評估爲event="click"的複選框/單選按鈕)

現在你可以在bean中擁有這個了。

public void changeNotified(Message message) { 
    // ... 
} 
+0

它的工作原理,謝謝!但是,爲什麼'cc.notifiedListener'而不是'cc.attrs.notifiedListener'? – Alex

+1

因爲'#{cc}'引用'YourComposite'實例。你想調用那裏的方法,而不是傳入的方法。 – BalusC

+0

好的,理解。謝謝!!! – Alex