我如何创建一个将 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?
我正在尝试创建一个允许用户在 h:singleSelectMenu 和 h:selectManyListbox 之间切换的复合组件。我有它的工作排序。只要值字段指向集合,它就可以工作...如果值字段为空,它就不起作用。
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");
}
}
我尝试写入属性映射,但没有成功
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 表达式变成了 "hardcoded" 值,无法再到达 bean 属性。
您应该获取 ValueExpression
并通过 setValue()
调用调用 setter。
getValueExpression("selected").setValue(context.getElContext(), selected);
与具体问题无关,在实现支持组件时,最好像 UI 组件一样思考,而不是支持 bean。不要直接操作模型(复合属性),而是通过 binding
访问复合组件自己的组件。此外,请勿触摸 getters/setters。将 <h:selectOneMenu value>
替换如下:
<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);
}
我正在尝试创建一个允许用户在 h:singleSelectMenu 和 h:selectManyListbox 之间切换的复合组件。我有它的工作排序。只要值字段指向集合,它就可以工作...如果值字段为空,它就不起作用。
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");
}
}
我尝试写入属性映射,但没有成功
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 表达式变成了 "hardcoded" 值,无法再到达 bean 属性。
您应该获取 ValueExpression
并通过 setValue()
调用调用 setter。
getValueExpression("selected").setValue(context.getElContext(), selected);
与具体问题无关,在实现支持组件时,最好像 UI 组件一样思考,而不是支持 bean。不要直接操作模型(复合属性),而是通过 binding
访问复合组件自己的组件。此外,请勿触摸 getters/setters。将 <h:selectOneMenu value>
替换如下:
<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);
}