将f:属性传递给f:复合实现中的ajax侦听器

时间:2015-07-11 10:34:06

标签: jsf attributes jsf-2.2 composite-component

我有以下复合组件(仅限相关代码):

    <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调用此合成:

<my:message message="#{msg}" notifiedListener="#{problem.changeNotified}"/>

更改事件在支持bean中如下处理:

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

问题是支持bean中的消息是null,尽管event.getComponent()是正确的(HTMLSelectBooleanCheckbox)。

为什么不设置属性?

谢谢你们

1 个答案:

答案 0 :(得分:2)

设置了属性(您可以通过确定getValueExpression("msg")未返回null来确认),但属性值只是评估null。延迟表达式在您想要获取值的时刻进行评估。因此,#{cc.attrs.message}在您在ajax侦听器方法中调用get("msg")的那一刻进行评估。但是,此时,#{cc}在EL范围内的任何位置都不存在。

确切地说,技术问题的解决方法是手动将其放回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");

无论哪种方式,这只是相当笨拙,因为支持bean不需要知道复合调用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"已经是正确的 - 它评估为public void changeNotified(Message message) { // ... } 对于复选框/ radiobuttons)

现在你可以在bean中使用它。

var words = Data.execute("select id,word from tbl_Words");
var means = Data.execute("select id,mean from tbl_Meanings");
var rndInd = Math.floor((Math.random() * words.rows.length) + 0);
Pages.pg_Main.lblWord.text = words.rows[rndInd][1];
var rmvItm = words.rows.splice(rndInd,1);