Jsf 使用「;请选择";f:selectItem在p:SelectOne菜单中具有空/空值

Jsf 使用「;请选择";f:selectItem在p:SelectOne菜单中具有空/空值,jsf,primefaces,converter,jsf-2.2,selectonemenu,Jsf,Primefaces,Converter,Jsf 2.2,Selectonemenu,我正在从数据库中填充一个,如下所示 <p:selectOneMenu id="cmbCountry" value="#{bean.country}" required="true" converter="#{countryConverter}"> <f:selectItem itemLabel="Select" itemValue="#{null}"/>

我正在从数据库中填充一个
,如下所示

<p:selectOneMenu id="cmbCountry" 
                 value="#{bean.country}"
                 required="true"
                 converter="#{countryConverter}">

    <f:selectItem itemLabel="Select" itemValue="#{null}"/>

    <f:selectItems var="country"
                   value="#{bean.countries}"
                   itemLabel="#{country.countryName}"
                   itemValue="#{country}"/>

    <p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>

<p:message for="cmbCountry"/>
<p:selectOneMenu id="cmbCountry"
                 value="#{stateManagedBean.selectedItem}"
                 required="true">

    <f:selectItem itemLabel="Select" itemValue="#{null}"/>

    <f:selectItems var="country"
                   converter="#{countryConverter}"
                   value="#{stateManagedBean.selectedItems}"
                   itemLabel="#{country.countryName}"
                   itemValue="${country}"/>
</p:selectOneMenu>

<p:message for="cmbCountry"/>

null
被一个空字符串替换,但当所讨论的对象是
null
时,返回一个空字符串,这反过来会导致另一个问题,如所示

如何使这种转换器正常工作


也尝试过使用
org.omnifaces.converter.SelectItemsConverter
,但没有什么不同。

你混合了一些东西,我不太清楚你想要实现什么,但让我们试试

这显然会导致抛出java.lang.NumberFormatException 在它的转换器中

里面没有什么明显的东西。若值为空或空字符串,则不签入转换器,并且应该签入。在这种情况下,转换器应该返回null

为什么它将Select(itemLabel)呈现为其值而不是空值 字符串(itemValue)

“选择”菜单必须已选择某些内容。如果不提供空值,将选择列表中的第一个元素,这不是您所期望的

只需将转换器修复为使用空/空字符串,并让JSF对返回的
null
作为不允许的值作出反应。首先调用转换,然后进行验证


我希望这能回答您的问题。

除了不完整之外,这个答案也被弃用,因为我在写这篇文章时使用的是Spring:

我已经修改了转换器的
getAsString()
方法,当没有发现类似的
Country
对象时(除了一些其他更改),将返回空字符串,而不是返回
null

这将生成以下HTML

<select id="form:cmbCountry_input" name="form:cmbCountry_input">
    <option value="" selected="selected">Select</option>
    <option value="56">Country1</option>
    <option value="55">Country2</option>
</select>

挑选
国家1
国家2
之前,生成的HTML看起来像

<select id="form:cmbCountry_input" name="form:cmbCountry_input">
    <option selected="selected">Select</option>
    <option value="56">Country1</option>
    <option value="55">Country2</option>
</select>

挑选
国家1
国家2
注意第一个
没有
属性

当选择第一个选项时(即使
require
设置为false),这也会像预期的那样绕过转换器工作。当
itemValue
更改为非
null
时,它的行为不可预测(我不理解这一点)

如果列表中的其他项目设置为非空值,并且转换器中接收的项目始终为空字符串(即使选择了其他选项),则无法选择列表中的其他项目

此外,在转换器中将此空字符串解析为
Long
时,引发
NumberFormatException
后产生的
ConverterException
不会在
UIViewRoot
中报告错误(至少应该发生这种情况)。相反,可以在服务器控制台上看到完整的异常堆栈跟踪


如果有人能透露一些信息,我会接受答案,如果答案是肯定的。

这对我来说完全有效:

<p:selectOneMenu id="cmbCountry"
                 value="#{bean.country}"
                 required="true"
                 converter="#{countryConverter}">

    <f:selectItem itemLabel="Select"/>

    <f:selectItems var="country"
                   value="#{bean.countries}"
                   itemLabel="#{country.countryName}"
                   itemValue="#{country}"/>

    <p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>

当select项值为
null
时,JSF将不会呈现
,而只呈现
。因此,浏览器将提交选项的标签。这在(我的重点)中有明确规定:

value=cdata[CS]

此属性指定控件的初始值如果未设置此属性,则初始值将设置为选项元素的内容。

您还可以通过查看HTTP流量监视器来确认这一点。您应该看到正在提交的选项标签

您需要将“选择项”值改为空字符串。然后JSF将呈现一个
。如果您使用的是转换器,那么当值为
null
时,您实际上应该从转换器返回一个空字符串
“”
。javadoc(我的重点)中也明确规定了这一点:

getAsString

返回:如果值为null,则返回长度为零的字符串,否则返回转换结果

因此,如果将
与这样的转换器结合使用,则会呈现
,浏览器将只提交一个空字符串,而不是选项标签

至于处理空字符串提交值(或
null
),实际上应该让转换器将此职责委托给
required=“true”
属性。因此,当传入的
null
或空字符串时,您应该立即返回
null
。基本上,实体转换器的实现应如下所示:

@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
    if (value == null) {
        return ""; // Required by spec.
    }

    if (!(value instanceof SomeEntity)) {
        throw new ConverterException("Value is not a valid instance of SomeEntity.");
    }

    Long id = ((SomeEntity) value).getId();
    return (id != null) ? id.toString() : "";
}

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
    if (value == null || value.isEmpty()) {
        return null; // Let required="true" do its job on this.
    }

    if (!Utils.isNumber(value)) {
        throw new ConverterException("Value is not a valid ID of SomeEntity.");
    }

    Long id = Long.valueOf(value);
    return someService.find(id);
}
至于你在这方面的特殊问题

但是,当所讨论的对象为null时返回空字符串,反过来又会导致另一个问题,如所示

正如那边所回答的,这是Mojarra中的一个bug,在OmniFaces 1.8中被绕过了
。因此,如果您至少升级到OmniFaces 1.8.3,并使用其
而不是
,那么您就不会再受到此错误的影响

OmniFaces
SelectItemsConverter
在这种情况下也可以正常工作。它返回一个空字符串,表示
null

  • 如果要避免所选组件的空值,最优雅的方法是使用
    noselection选项
noSelectionOption=“true”
时,转换器甚至不会尝试处理该值

另外,当您将其与
结合使用时,当用户尝试选择该选项时,您将得到一个验证错误

最后一次触摸,您可以使用
itemsabled
属性向用户明确说明他不能使用此选项

<p:selectOneMenu id="cmbCountry"
                 value="#{bean.country}"
                 required="true"
                 converter="#{countryConverter}">

    <f:selectItem itemLabel="Select"
                  noSelectionOption="true"
                  itemDisabled="true"/>

    <f:selectItems var="country"
                   value="#{bean.countries}"
                   itemLabel="#{country.countryName}"
                   itemValue="#{country}"/>

    <p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>

<p:message for="cmbCountry"/>

  • 现在,如果您确实希望能够设置空值,您可以使用“欺骗”转换器以返回空值

    <f:selectItem itemLabel="Select" itemValue="" />
    
    
    
更多的阅读,或者


getAsO中
<select id="form:cmbCountry_input" name="form:cmbCountry_input">
    <option selected="selected">Select</option>
    <option value="56">Country1</option>
    <option value="55">Country2</option>
</select>
<p:selectOneMenu id="cmbCountry"
                 value="#{bean.country}"
                 required="true"
                 converter="#{countryConverter}">

    <f:selectItem itemLabel="Select"/>

    <f:selectItems var="country"
                   value="#{bean.countries}"
                   itemLabel="#{country.countryName}"
                   itemValue="#{country}"/>

    <p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
@Controller
@Scope("request")
public final class CountryConverter implements Converter {

    @Autowired
    private final transient Service service = null;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.trim().equals("")) {
            return null;
        }
        //....
        // No change
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return value == null ? null : value instanceof Country ? ((Country) value).getCountryId().toString() : null;
        //**** Returns an empty string, when no Country is found ---> wrong should return null, don't care about the rendering.
    }
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
    if (value == null) {
        return ""; // Required by spec.
    }

    if (!(value instanceof SomeEntity)) {
        throw new ConverterException("Value is not a valid instance of SomeEntity.");
    }

    Long id = ((SomeEntity) value).getId();
    return (id != null) ? id.toString() : "";
}

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
    if (value == null || value.isEmpty()) {
        return null; // Let required="true" do its job on this.
    }

    if (!Utils.isNumber(value)) {
        throw new ConverterException("Value is not a valid ID of SomeEntity.");
    }

    Long id = Long.valueOf(value);
    return someService.find(id);
}
<p:selectOneMenu id="cmbCountry"
                 value="#{bean.country}"
                 required="true"
                 converter="#{countryConverter}">

    <f:selectItem itemLabel="Select"
                  noSelectionOption="true"
                  itemDisabled="true"/>

    <f:selectItems var="country"
                   value="#{bean.countries}"
                   itemLabel="#{country.countryName}"
                   itemValue="#{country}"/>

    <p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>

<p:message for="cmbCountry"/>
<f:selectItem itemLabel="Select" itemValue="" />
public void limparSelecao(AjaxBehaviorEvent evt) {
    Object submittedValue = ((UIInput)evt.getSource()).getSubmittedValue();

    if (submittedValue != null) {
        getPojo().setTipoCaixa(null);
    }
}
<p:selectOneMenu id="tipo"
                 value="#{cadastroCaixaMonitoramento.pojo.tipoCaixa}" 
                 immediate="true"
                 required="true"
                 valueChangeListener="#{cadastroCaixaMonitoramento.selecionarTipoCaixa}">

    <f:selectItem itemLabel="Selecione" itemValue="SELECIONE" noSelectionOption="false"/>

    <f:selectItems value="#{cadastroCaixaMonitoramento.tiposCaixa}" 
                   var="tipo" itemValue="#{tipo}"
                   itemLabel="#{tipo.descricao}" />

    <p:ajax process="tipo"
            update="iten_monitorado"
            event="change" listener="#{cadastroCaixaMonitoramento.limparSelecao}" />
</p:selectOneMenu>