如何创建将Collection绑定到h:selectOneMenu和h:selectManyListbox的JSF复合组件? [英] How can I create a JSF composite component that binds a Collection to both an h:selectOneMenu and h:selectManyListbox?

查看:75
本文介绍了如何创建将Collection绑定到h:selectOneMenu和h:selectManyListbox的JSF复合组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个复合组件,该组件将允许用户在h:singleSelectMenu和h:selectManyListbox之间切换.我有它的工作.只要value字段指向Collection,它就起作用...如果value字段为null,则不起作用.

I'm trying to create a composite component that will allow the user to toggle between a h:singleSelectMenu and h:selectManyListbox. I have it working sort of. It works as long as the value field points to a Collection...it does NOT work if the value field is null.

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: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}"
                             value="#{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 {    

    public SingleMultiSelect() {
        super();
    }           

    /**
     * Converts the Object selected within the selectOneMenu to the list
     * used by the component.
     * 
     * @param singleSelected
     */
    public void setSingleSelected(Object singleSelected) {
        getSelected().clear();
        if(singleSelected != null) {
            getSelected().add(singleSelected);
        }
    }

    /**
     * Converts the collection used by the component to a single
     * Object selected within the selectOneMenu.
     * 
     * @return
     */
    public Object getSingleSelected() {
        return getSelected().size() > 0 ? getSelected().iterator().next() : null;
    }    

    private Collection getSelected() {
        return (Collection) getAttributes().get("selected");
    }
}

我尝试写入属性映射,但这没用

I tried writing to the attributes map but that didn't work

public void setSingleSelected(Object singleSelected) {
    HashSet selected = new HashSet();         
    selected.add(singleSelected);
    ((Collection) getAttributes()).put("selected", selected);
}

推荐答案

getAttributes().put("selected", selected);

您基本上是用HashSet覆盖基础的ValueExpression对象#{cc.attrs.selected}.换句话说,EL表达式成为一个硬编码"值,并且不再具有bean属性.

You're basically overriding the underlying ValueExpression object #{cc.attrs.selected} with a HashSet. In other words, the EL expression became a "hardcoded" value and can't reach the bean property anymore.

您应该获取ValueExpression并通过setValue()调用来调用setter.

You should be obtaining the ValueExpression and invoke the setter via setValue() call.

getValueExpression("selected").setValue(context.getElContext(), selected);


无关与具体问题无关,在实现支持组件时,最好像UI组件而不是支持bean那样思考.不要直接操作模型(复合属性),而是要通过binding到达复合组件自己的组件.此外,请勿触摸吸气剂/吸气剂.替换<h:selectOneMenu value>如下:


Unrelated to the concrete problem, when implementing a backing component, it's better to think like an UI component, not a backing bean. Don't manipulate the model directly (the composite attributes), but the composite component's own components which you reach via binding. Moreover, do not touch the getters/setters. Replace the <h:selectOneMenu value> as below:

<h:selectOneMenu binding="#{cc.singleSelected}" ...>

private UISelectOne singleSelected; // +getter+setter

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

这篇关于如何创建将Collection绑定到h:selectOneMenu和h:selectManyListbox的JSF复合组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆