Using a "Please select" f:selectItem with null/empty value inside a p:selectOneMenu
When the select item value is null
, then JSF won't render <option value>
, but only <option>
. As consequence, browsers will submit the option's label instead. This is clearly specified in HTML specification (emphasis mine):
value = cdata [CS]
This attribute specifies the initial value of the control. If this attribute is not set, the initial value is set to the contents of the OPTION element.
You can also confirm this by looking at HTTP traffic monitor. You should see the option label being submitted.
You need to set the select item value to an empty string instead. JSF will then render a <option value="">
. If you're using a converter, then you should actually be returning an empty string ""
from the converter when the value is null
. This is also clearly specified in Converter#getAsString()
javadoc (emphasis mine):
getAsString
...
Returns: a zero-length String if value is null, otherwise the result of the conversion
So if you use <f:selectItem itemValue="#{null}">
in combination with such a converter, then a <option value="">
will be rendered and the browser will submit just an empty string instead of the option label.
As to dealing with the empty string submitted value (or null
), you should actually let your converter delegate this responsibility to the required="true"
attribute. So, when the incoming value
is null
or an empty string, then you should return null
immediately. Basically your entity converter should be implemented like follows:
@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);
}
As to your particular problem with this,
but returning an empty string when the object in question is null, in turn incurs another problem as demonstrated here.
As answered over there, this is a bug in Mojarra and bypassed in <o:viewParam>
since OmniFaces 1.8. So if you upgrade to at least OmniFaces 1.8.3 and use its <o:viewParam>
instead of <f:viewParam>
, then you shouldn't be affected anymore by this bug.
The OmniFaces SelectItemsConverter
should also work as good in this circumstance. It returns an empty string for null
.
- If you want to avoid null values for your select component, the most elegant way is to use the
noSelectionOption
.
When noSelectionOption="true"
, the converter will not even try to process the value.
Plus, when you combine that with <p:selectOneMenu required="true">
you will get a validation error, when user tries to select that option.
One final touch, you can use the itemDisabled
attribute to make it clear to the user that he can't use this option.
<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"/>
-
Now if you do want to be able to set a null value, you can 'cheat' the converter to return a null value, by using
<f:selectItem itemLabel="Select" itemValue="" />
More reading here, here, or here
You're mixing a few things, and it's not fully clear to me what you want to achieve, but let's try
This obviously causes the java.lang.NumberFormatException to be thrown in its converter.
It's nothing obvious in it. You don't check in converter if value is empty or null String, and you should. In that case the converter should return null.
Why does it render Select (itemLabel) as its value and not an empty string (itemValue)?
The select must have something selected. If you don't provide empty value, the first element from list would be selected, which is not something that you would expect.
Just fix the converter to work with empty/null strings and let the JSF react to returned null
as not allowed value. The conversion is called first, then comes the validation.
I hope that answers your questions.