I have an h:selectOneRadio
component that I have mapped to a Boolean
(not boolean) in the backing bean. On load, the radio buttons do not have a default select option, and the radio button is not a required field.
Example code:
JSF page excerpt:
<h:form>
<h:selectOneRadio value="#{pagecode.test}">
<f:selectItem itemValue="#{true}" itemLabel="Yes"/>
<f:selectItem itemValue="#{false}" itemLabel="No"/>
</h:selectOneRadio>
<h:commandButton value="Save" action="#{pagecode.save}"/>
</h:form>
Backing Java pagecode:
package pagecode;
public class JSFBooleanTestView extends PageCodeBase {
protected Boolean test;
public Boolean getTest() {
return test;
}
public void setTest(Boolean test) {
this.test = test;
}
public String save() {
return "JSFBooleanTestView";
}
}
faces-config.xml excerpt:
<managed-bean>
<managed-bean-name>pagecode</managed-bean-name>
<managed-bean-class>pagecode.JSFBooleanTestView</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Since test
is not being defaulted to a value, the radio buttons start off unselected. Since the field is not required, my expectation was that pressing the Save button with no radio button selected would lead to test being null. Instead, test is assigned false.
Different options that I have tried that have had no effect:
Setting h:selectOneRadio required="false"
Adding to web.xml:
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Adding to web.xml:
<context-param>
<param-name>org.apache.el.parser.COERCE_TO_ZERO</param-name>
<param-value>false</param-value>
</context-param>
What did work was adding immediate="true"
to h:commandButton
.
My questions:
What is making h:selectOneRadio
convert nulls to false normally? Is that the intended behavior?
Why does immediate="true"
make h:commandButton
not convert the nulls to false? What exactly is immediate="true"
doing differently in this particular situation that is causing the difference? I understand that the immediate="true"
will skip certain phases in the JSF lifecycle, but I don't understand what in those phases is causing the conversion from null to false to occur.
I just realized I added immediate="true"
to the h:commandButton
, not the h:selectOneRadio
. My question has been edited accordingly.
This is used in a portlet application on IBM WebSphere Portal 8.0 using Apache MyFaces 2.0.
The behavior of primitive wrappers like Boolean
, Integer
, Double
, Character
, etc getting primitive's default values of false
, 0
, 0.0
, \u0000
, etc assigned instead of null
is specific to Apache EL, which is used in a.o. Tomcat, JBoss AS and WebSphere servers. It affects only EL 2.1 and EL 2.2 based versions. Since EL 3.0 this misbehavior is corrected (after my own issue report).
Using org.apache.el.parser.COERCE_TO_ZERO
is indeed the solution, but you should set it as a VM argument (system property), not as a context parameter. For this specific problem, the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
is unrelated, but you should most definitely keep this in order to avoid String
typed values being set with empty strings instead of null
.
All of this and a bit of history is detailed in the blog article The empty String madness.