如果 JSF 复合组件与 f:ajax 标记一起使用,我如何在 EL 中进行测试?

How can I test, inside EL, if a JSF composite component is used with an f:ajax tag?

我的复合组件使用 clientBehavior 标签可选地支持 f:ajax 标签:

singleMultiSelect.xhtml

<cc:interface componentType="singleMultiSelect">

        <!-- The initial list of objects -->
        <cc:attribute name="list" type="java.util.List" required="true"/>
        <!-- The selected objects -->
        <cc:attribute name="selected" type="java.util.Collection" required="true"/>
        <!-- whether to display the selectOneMenu (true) or selectManyBox (false) -->
        <cc:attribute name="singleSelect" type="java.lang.Boolean" 
                  required="false" default="true"/>  
        <cc:clientBehavior name="valueChange" event="valueChange"
                       targets="selectOneMenu selectManyListbox"/>

    </cc:interface>
    <cc:implementation>

        <span id="#{cc.clientId}">

            <ace:checkboxButton id="singleSelectChkBx"
                                value="#{cc.attrs.singleSelect}">
                <ace:ajax render="#{cc.clientId}"/>
            </ace:checkboxButton>                    

            <h:selectOneMenu id="selectOneMenu" 
                             rendered="#{cc.attrs.singleSelect}"
                             binding="#{cc.singleSelected}">                    
                <f:selectItems value="#{cc.attrs.list}"/>

                <!-- THIS PART IS NOT WORKING BECAUSE IT RETURNS 0
                     NO MATTER IF F:AJAX IS USED OR NOT -->
                <c:if test="#{cc.getClientBehaviors().size() > 0}">
                    <f:ajax event="valueChange" execute="@this"
                            listener="#{cc.updateSelected}"/>
                </c:if>
            </h:selectOneMenu>

            <h:selectManyListbox id="selectManyListbox" 
                                 rendered="#{! cc.attrs.singleSelect}"
                                 value="#{cc.attrs.selected}">
                <f:selectItems value="#{cc.attrs.list}"/>
            </h:selectManyListbox>

        </span>

    </cc:implementation>
</ui:composition>

SingleMultiSelect.java

public class SingleMultiSelect extends UINamingContainer {

    private UISelectOne singleSelected;

    public SingleMultiSelect() {
        super();
    }

    @Override
    public void processUpdates(FacesContext context) {
        super.processUpdates(context);

        if (getAttributes().get("singleSelect") == Boolean.TRUE) {
            HashSet selected = new HashSet();
            if(singleSelected.getValue() != null) {
                selected.add(singleSelected.getValue());
            }
            getValueExpression("selected").setValue(context.getELContext(), selected);
        }
    }

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        if (getAttributes().get("singleSelect") == Boolean.TRUE) {
            Collection selected = (Collection) getAttributes().get("selected");
            if (selected != null && !selected.isEmpty()) {
                singleSelected.setValue(selected.iterator().next());
            } else {
                singleSelected.setValue(null);
            }
        }

        super.encodeBegin(context);
    }

    public void updateSelected(AjaxBehaviorEvent event) {

        FacesContext context = FacesContext.getCurrentInstance();

        if (getAttributes().get("singleSelect") == Boolean.TRUE) {
            HashSet selected = new HashSet();
            if(singleSelected.getValue() != null) {
                selected.add(singleSelected.getValue());
            }
            getValueExpression("selected").setValue(context.getELContext(), selected);
        }
    }

    /**
     * @return the singleSelected
     */
    public UISelectOne getSingleSelected() {
        return singleSelected;
    }

    /**
     * @param singleSelected the singleSelected to set
     */
    public void setSingleSelected(UISelectOne singleSelected) {
        this.singleSelected = singleSelected;
    }
}

我如何在 EL 中测试客户端是否实际使用 ajax。例如:

<mylib:mycomp />

<mylib:mycomp>
    <f:ajax event="valueChange"/>
</mylib:mycomp>

在代码中,您可以看到我尝试使用 cc.getClientBehaviors().size() 来查看是否填充了客户端行为,但它总是 returns 0.

您可以检查复合组件代码内部,例如这些属性。

1) 如果插入了特定行为

#{empty cc.getClientBehaviors().get("valueChange")}

2) 或任何

#{cc.getClientBehaviors().size() > 0}

JSF 组件必须实现 ClientBehaviorHolder 接口并且 getEventNames() 方法需要重写为 return支持的事件列表:

SingleMultiSelect.java:

public class SingleMultiSelect extends UINamingContainer implements ClientBehaviorHolder {

    ...

    @Override
    public Collection<String> getEventNames() {
        Collection<String> eventNames = new HashSet<>();
        eventNames.add("valueChange");
        return eventNames;
    }

    ...
}

一旦完成,@rvillar 的回答就奏效了:

1) 如果插入了特定行为

#{empty cc.getClientBehaviors().get("valueChange")}

2) 或任何

#{cc.getClientBehaviors().size() > 0}