如何向将集合绑定到 h:selectOneMenu 和 h:selectManyListbox 的复合组件添加 f:ajax 支持?

How can I add f:ajax support to my composite component that binds a Collection to an h:selectOneMenu and h:selectManyListbox?

这是以下内容的后续:

我有一个复合组件,允许用户在 h:selectOneMenu 和 h:selectManyListbox 和 returns 集合之间切换作为 "selected" 值。这很好用。我现在正在尝试向它添加 ajax 支持,这样,如果在菜单或列表框中选择了一个值,就会触发 ajax 事件(意思是 mySelected 应该填充它的值,我可以在我想要的页面上重新呈现任何其他组件。

<swr:singleMultiSelect list="#{myBean.myList}"
                       selected="#{myBean.mySelected}"
                       singleSelect="#{myBean.singleSelect}">
    <f:ajax event="valueChange" execute="@this" 
            render="buttonPanel"/>
</swr:singleMultiSelect>

在我的复合组件中具有以下属性:

<cc:clientBehavior name="valueChange" event="valueChange"
                   targets="selectOneMenu selectManyListbox"/>

我在使用 selectOneMenu 的值填充 mySelected 时遇到问题。当我提交非 ajax 表单时,processUpdates 会处理这个...我不太确定如何让它为 [=43 做同样的事情=] 事件虽然。

这是我的复合组件代码:

singleMultiSelect.xhtml

<?xml version="1.0" encoding="US-ASCII"?>
<!DOCTYPE html>
<ui:composition
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:c="http://java.sun.com/jstl/core"
    xmlns:ace="http://www.icefaces.org/icefaces/components"
    >

    <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}"/>
            </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);
    }
    /**
     * @return the singleSelected
     */
    public UISelectOne getSingleSelected() {
        return singleSelected;
    }

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

向带有侦听器(绑定到 FacesComponent 中的方法)的 selectOneMenu 添加了一个 ajax 标记,它会更新选定的集合:

singleMultiSelect.xhtml:

<h:selectOneMenu id="selectOneMenu" 
                 rendered="#{cc.attrs.singleSelect}"
                 binding="#{cc.singleSelected}">                    
    <f:selectItems value="#{cc.attrs.list}"/>
    <f:ajax event="valueChange" execute="@this"
                    listener="#{cc.updateSelected}"/>
</h:selectOneMenu>

SingleMultiSelect.java:

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