javaserverfaces
  1. javaserverfaces
  2. JAVASERVERFACES-765

Immediately process update HtmlInputHidden values during conversion/validation phase

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Incomplete
    • Affects Version/s: 2.0.0 EDR1
    • Fix Version/s: unscheduled
    • Component/s: None
    • Labels:
      None
    • Environment:

      Operating System: All
      Platform: All

    • Issuezilla Id:
      765

      Description

      Imagine a request scoped bean with a h:inputHidden field and another UIInput
      field which is to be converted and/or validated. If the h:inputHidden already
      has a value but the conversion and/or validation of the another UIInput field
      fails, then the value of the h:inputHidden will get lost.

      Truly, this is behaviour by specification, but it is not very intuitive. One
      would expect that hidden input values (read: the values which aren't to be
      filled in by the client, but by the server) should retain its value. In real
      world web applications the sole purpose of hidden input elements is to transfer
      values from request to request without any client interaction.

      Thus, hereby my request for an enhancement in the API: make sure that the API
      retains hidden input values regardless of the global outcome of the
      conversion/validation phase. And only if the hidden input value itself is
      already successfully converted and validated.

      In theory this can simply be achieved by adding the following lines at the
      bottom of the if (isValid()) {} block which starts at line 887 of
      javax.faces.component.UIInput in 2.0.0 EDR1:

      if (this instanceof HtmlInputHidden) {
      processUpdates(context);
      }

      If necessary you can also let it implement a certain marker interface, e.g.
      RetainableValueHolder or so (I just say something) so that one can decide to let
      some custom component implement it.

      I am almost sure that this will be greatly appreciated by the web development
      world. It is certainly much more intuitive. And it is much more elegant than the
      'workaround' to bind the h:inputHidden and using the UIInput#getValue() /
      setValue() instead.

        Activity

        Hide
        Jason Lee added a comment -

        Spec enhancements should be filed under the spec issue tracker. I have copied
        your request over and added you to the CC list:

        https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=437

        I'll close this out here, and we can track the issue on the other tracker.

        Thanks for the feedback!

        Show
        Jason Lee added a comment - Spec enhancements should be filed under the spec issue tracker. I have copied your request over and added you to the CC list: https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=437 I'll close this out here, and we can track the issue on the other tracker. Thanks for the feedback!
        Hide
        balusc added a comment -

        OK, thank you.

        Show
        balusc added a comment - OK, thank you.
        Hide
        balusc added a comment -

        Sorry that I continue the discussion here, but I can't find any way to respond
        in the new issue in the other thread.

        Well, I wanted to say sorry; the story behind this issue is actually invalid.
        The hidden values will actually be retained in the response after
        conversion/validation error, but only by UIInput#getSubmittedValue(). I totally
        missed this. I was testing it by getting the hidden property in a h:outputText.

        The main reason that I posted this issue was that I used a hidden property in
        the rendered attribute of another component, i.e. #

        {myBean.hiddenValue != null}

        where myBean is request scoped. The component disappeared when a
        conversion/valdation error was raised.

        Please note the following testcase of a not uncommon use case: a DTO object with
        an ID which is not to be edited by the user and several fields which are
        presented for edit. The ID is transferred to the next request using
        h:inputHidden. Its value in h:outputText will disappear when a
        conversion/validation error occur.

        MyBean
        ----------------------------------------------
        public class MyBean {

        private MyData myData;

        public MyBean() {
        myData = new MyData();

        if (FacesContext.getCurrentInstance().getRenderResponse())

        { // A (w)hacky way to preset MyData object for the 1st view. // Just for testing purposes. In real this is to be obtained // from the previous request as selected row of a dataTable or so. myData.setId(Long.valueOf(1)); myData.setName("foo"); myData.setValue(Integer.valueOf(123)); }

        }

        public void submit()

        { System.out.println("submit: " + myData.getId() + "," + myData.getName() + "," + myData.getValue()); }

        public MyData getMyData()

        { return myData; }

        }
        ----------------------------------------------

        MyData
        ----------------------------------------------
        public class MyData

        { private Long id; private String name; private Integer value; // With usual getters and setters. }

        ----------------------------------------------

        JSF
        ----------------------------------------------
        <h:form id="form">
        <h:panelGrid columns="3">
        <h:outputLabel for="id" value="ID" />
        <h:outputText id="id" value="#

        {myBean.myData.id}" />
        <h:message for="id" />

        <h:outputLabel for="name" value="Name" />
        <h:inputText id="name" value="#{myBean.myData.name}" required="true" />
        <h:message for="name" />

        <h:outputLabel for="value" value="Value" />
        <h:inputText id="value" value="#{myBean.myData.value}" required="true" />
        <h:message for="value" />

        <h:inputHidden id="hidden" value="#{myBean.myData.id}

        " />
        <h:commandButton value="submit" action="#

        {myBean.submit}

        " />
        <h:panelGroup />
        </h:panelGrid>
        </h:form>
        ----------------------------------------------

        To test it, remove one or both of the 'name' and 'value' values, or enter an
        invalid number as 'value' to cause a validation error on submit. You'll see that
        the ID value disappears.

        As an workaround I could use h:inputText readonly="true" for the ID value. But
        this not very intuitive.

        Maybe this new post makes the 'issue' more understandable.

        Show
        balusc added a comment - Sorry that I continue the discussion here, but I can't find any way to respond in the new issue in the other thread. Well, I wanted to say sorry; the story behind this issue is actually invalid. The hidden values will actually be retained in the response after conversion/validation error, but only by UIInput#getSubmittedValue(). I totally missed this. I was testing it by getting the hidden property in a h:outputText. The main reason that I posted this issue was that I used a hidden property in the rendered attribute of another component, i.e. # {myBean.hiddenValue != null} where myBean is request scoped. The component disappeared when a conversion/valdation error was raised. Please note the following testcase of a not uncommon use case: a DTO object with an ID which is not to be edited by the user and several fields which are presented for edit. The ID is transferred to the next request using h:inputHidden. Its value in h:outputText will disappear when a conversion/validation error occur. MyBean ---------------------------------------------- public class MyBean { private MyData myData; public MyBean() { myData = new MyData(); if (FacesContext.getCurrentInstance().getRenderResponse()) { // A (w)hacky way to preset MyData object for the 1st view. // Just for testing purposes. In real this is to be obtained // from the previous request as selected row of a dataTable or so. myData.setId(Long.valueOf(1)); myData.setName("foo"); myData.setValue(Integer.valueOf(123)); } } public void submit() { System.out.println("submit: " + myData.getId() + "," + myData.getName() + "," + myData.getValue()); } public MyData getMyData() { return myData; } } ---------------------------------------------- MyData ---------------------------------------------- public class MyData { private Long id; private String name; private Integer value; // With usual getters and setters. } ---------------------------------------------- JSF ---------------------------------------------- <h:form id="form"> <h:panelGrid columns="3"> <h:outputLabel for="id" value="ID" /> <h:outputText id="id" value="# {myBean.myData.id}" /> <h:message for="id" /> <h:outputLabel for="name" value="Name" /> <h:inputText id="name" value="#{myBean.myData.name}" required="true" /> <h:message for="name" /> <h:outputLabel for="value" value="Value" /> <h:inputText id="value" value="#{myBean.myData.value}" required="true" /> <h:message for="value" /> <h:inputHidden id="hidden" value="#{myBean.myData.id} " /> <h:commandButton value="submit" action="# {myBean.submit} " /> <h:panelGroup /> </h:panelGrid> </h:form> ---------------------------------------------- To test it, remove one or both of the 'name' and 'value' values, or enter an invalid number as 'value' to cause a validation error on submit. You'll see that the ID value disappears. As an workaround I could use h:inputText readonly="true" for the ID value. But this not very intuitive. Maybe this new post makes the 'issue' more understandable.
        Hide
        Manfred Riem added a comment -

        Closing issue out

        Show
        Manfred Riem added a comment - Closing issue out

          People

          • Assignee:
            javaserverfowner
            Reporter:
            balusc
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: