2015-07-11 58 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