javaserverfaces
  1. javaserverfaces
  2. JAVASERVERFACES-2262

"Interpret empty string submitted value as null" must also be considered in HtmlBasicRenderer#getCurrentValue()

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Won't Fix
    • Affects Version/s: 2.1.3
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None
    • Environment:

      Mojarra 2.1.4

      Description

      Consider this testcase:

      <h:form>
          <h:inputText value="#{bean.input}" required="true" />
          <h:commandButton value="submit" />
          <h:messages />
      </h:form>
      

      with

      private String input = "input";
      // Getter+Setter
      

      When you remove the value, the "value is required" validation error appears, which is perfectly fine. Also note that the input field is kept blank, as the enduser originally entered, which makes totally sense.

      However, when you add this context parameter (which is often done to avoid the non-required model values being cluttered with empty strings and/or in order to be able to use JSR303 @NotNull annotation)

      <context-param>
          <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
          <param-value>false</param-value>
      </context-param>
      

      then the initial model value is redisplayed on validation error instead of the submitted value.

      The culprit is in HtmlBasicRenderer#getCurrentValue().

      if (component instanceof UIInput) {
          Object submittedValue = ((UIInput) component).getSubmittedValue();
          if (submittedValue != null) {
              // value may not be a String...
              return submittedValue.toString();
          }
      }
      

      One possible solution would be to check as well if it is invalid:

      if (component instanceof UIInput && !((UIInput) component).isValid()) {
          Object submittedValue = ((UIInput) component).getSubmittedValue();
          if (submittedValue != null) {
              // value may not be a String...
              return submittedValue.toString();
          } else {
              return null;
          }
      }
      

      This is actually also more in line with the normal behaviour of UIInput#validate() method.

        Activity

        Hide
        bfrevel added a comment -

        I have the same behavior in all components. For example the selectOneMenu component.

        This is my testcase:

        TestBean.java
        public class TestBean {
            @NotNull
            private String stringValue1;
            @NotNull
            private String stringValue2;
            @NotNull
            private Integer integerValue;
        
            //getter and setter methods
        
            private void init() {
        	stringValue1 = "test";
        	stringValue2 = "String1";
        	integerValue = 1;
            }
            
            public TestBean() {
        	this.init();
            }
        }
        
        page1.xhtml
        ...
        <h:outputLabel value="StringValue1:" for="stringValue1" />
        <h:inputText id="stringValue1" value="#{testBean.stringValue1}" />
        
        <h:outputLabel value="StringValue2:" for="stringValue2" />
        <h:selectOneMenu value="#{testBean.stringValue2}" id="stringValue2">
            <f:selectItem itemLabel="null" noSelectionOption="true" />
            <f:selectItem itemLabel="String1" itemValue="String1" />
            <f:selectItem itemLabel="String2" itemValue="String2" />
        </h:selectOneMenu>
        
        <h:outputLabel value="IntegerValue:" for="integerValue" />
        <h:selectOneMenu value="#{testBean.integerValue}" id="integerValue">
            <f:selectItem itemLabel="null" noSelectionOption="true" />
            <f:selectItem itemLabel="Integer1" itemValue="1" />
            <f:selectItem itemLabel="Integer2" itemValue="2" />
        </h:selectOneMenu>
        
        <h:commandButton id="save" action="#{testBean.save}" value="Save" />
        ...
        

        For all components and values we have the same behavior. If you select the null value and submit the form, JSF will redisplay the old value and not the submitted value.

        Show
        bfrevel added a comment - I have the same behavior in all components. For example the selectOneMenu component. This is my testcase: TestBean.java public class TestBean { @NotNull private String stringValue1; @NotNull private String stringValue2; @NotNull private Integer integerValue; //getter and setter methods private void init() { stringValue1 = "test" ; stringValue2 = "String1" ; integerValue = 1; } public TestBean() { this .init(); } } page1.xhtml ... <h:outputLabel value= "StringValue1:" for = "stringValue1" /> <h:inputText id= "stringValue1" value= "#{testBean.stringValue1}" /> <h:outputLabel value= "StringValue2:" for = "stringValue2" /> <h:selectOneMenu value= "#{testBean.stringValue2}" id= "stringValue2" > <f:selectItem itemLabel= " null " noSelectionOption= " true " /> <f:selectItem itemLabel= "String1" itemValue= "String1" /> <f:selectItem itemLabel= "String2" itemValue= "String2" /> </h:selectOneMenu> <h:outputLabel value= "IntegerValue:" for = "integerValue" /> <h:selectOneMenu value= "#{testBean.integerValue}" id= "integerValue" > <f:selectItem itemLabel= " null " noSelectionOption= " true " /> <f:selectItem itemLabel= "Integer1" itemValue= "1" /> <f:selectItem itemLabel= "Integer2" itemValue= "2" /> </h:selectOneMenu> <h:commandButton id= "save" action= "#{testBean.save}" value= "Save" /> ... For all components and values we have the same behavior. If you select the null value and submit the form, JSF will redisplay the old value and not the submitted value.
        Hide
        Werner Mueller added a comment -

        I've used the patch above and the described issue disappears.
        I've only tested it with inputText components, if I stumble across a selectMenu i'll try that out too.

        Show
        Werner Mueller added a comment - I've used the patch above and the described issue disappears. I've only tested it with inputText components, if I stumble across a selectMenu i'll try that out too.
        Hide
        rogerk added a comment -

        Did you mean "true" for javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL context param?
        When it is "false", there is no problem.

        Show
        rogerk added a comment - Did you mean "true" for javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL context param? When it is "false", there is no problem.
        Hide
        Werner Mueller added a comment -

        yes that is written in the description and also true in my environment.

        meanwhile: I tried mojarra 2.1.6 and I cannot reproduce this issue there. Although the patch here was not added something else must have changed.

        Show
        Werner Mueller added a comment - yes that is written in the description and also true in my environment. meanwhile: I tried mojarra 2.1.6 and I cannot reproduce this issue there. Although the patch here was not added something else must have changed.
        Hide
        balusc added a comment - - edited

        Yes, I meant to write <param-value>true</param-value>, sorry for the confusion. I can't edit the original ticket anymore.

        Another, maybe better, way to fix this issue is to alter UIInput#getSubmittedValue():

        public Object getSubmittedValue() {
            if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) {
                return "";
            }
            else {
                return submittedValue;
            }
        }
        

        This way the component libraries which rely on UIInput doesn't need to change their renderers.

        Show
        balusc added a comment - - edited Yes, I meant to write <param-value>true</param-value> , sorry for the confusion. I can't edit the original ticket anymore. Another, maybe better, way to fix this issue is to alter UIInput#getSubmittedValue() : public Object getSubmittedValue() { if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) { return ""; } else { return submittedValue; } } This way the component libraries which rely on UIInput doesn't need to change their renderers.
        Hide
        rogerk added a comment -
        Show
        rogerk added a comment - See specification issue: http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-939
        Hide
        cagatay_civici added a comment -

        considerEmptyStringNull must be considered, without it the initial proposed patch will fail for a case like;

        <h:inputText id="name" value="#{bean.input}" />
                            
        <h:inputText id="sname" value="#{bean.input2}" required="true" />
        					
        <h:commandButton id="btn" value="Submit" />
        

        If name is cleared and form is submitted, validation error happens because of sname field but name still displays "input" model value.

        I haven't tested Balusc's improved patch in last comment.

        Show
        cagatay_civici added a comment - considerEmptyStringNull must be considered, without it the initial proposed patch will fail for a case like; <h:inputText id= "name" value= "#{bean.input}" /> <h:inputText id= "sname" value= "#{bean.input2}" required= " true " /> <h:commandButton id= "btn" value= "Submit" /> If name is cleared and form is submitted, validation error happens because of sname field but name still displays "input" model value. I haven't tested Balusc's improved patch in last comment.
        Hide
        cagatay_civici added a comment -

        The patch I added to PrimeFaces is;

        if(component instanceof EditableValueHolder) {
                        EditableValueHolder input = (EditableValueHolder) component;
                        Object submittedValue = input.getSubmittedValue();
                        
                        if(ComponentUtils.considerEmptyStringAsNull(context) && submittedValue == null && context.isValidationFailed()) {
                            return null;
                        }
                        else if(submittedValue != null) {
                            return submittedValue.toString();
                        }
        }
        

        So we need to check if another input or current input itself caused validation error as well.

        Show
        cagatay_civici added a comment - The patch I added to PrimeFaces is; if (component instanceof EditableValueHolder) { EditableValueHolder input = (EditableValueHolder) component; Object submittedValue = input.getSubmittedValue(); if (ComponentUtils.considerEmptyStringAsNull(context) && submittedValue == null && context.isValidationFailed()) { return null ; } else if (submittedValue != null ) { return submittedValue.toString(); } } So we need to check if another input or current input itself caused validation error as well.

          People

          • Assignee:
            Unassigned
            Reporter:
            balusc
          • Votes:
            5 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: