12 Replies Latest reply on Oct 6, 2008 7:38 PM by igor_d

    Incorrect getClientId and getSubmittedvalue in Richfaces ver

    igor_d

      Hi dear all!

      In the new version of RichFaces you have overriden the old faces components with Richfaces's ones (e.g. javax.faces.component.html.HtmlInputText
      with org.richfaces.component.html.HtmlInputText).

      We have some problems with that:

      1) In version 3.1.* we used to call inputComponent.getSubmittedValue() in PhaseListener to check the submitted value, but in the new version it is always null. Is that the correct behaviour? If yes, how can we obtain the submitted value?

      2) To define the client id of invalid control we used to call:
      facesContext.addMessage(input.getClientId(facesContext), createMessage(invalidValues));
      That used to populate the correct clientId. But, if we have a repeater (<a4j:repeat>) in xhtml, than the client id returned for that component becomes incorrect (which was not the case in version 3.1.*).
      E.g.:

       <a4j:repeat id="customers" value="#{customers}" var="party">
       <h:inputText id="fullName"
       required="true"
       requiredMessage="party.mainName.isNull"
       value="#{party.nameMainNameText}"/>
      

      In ver. 3.1.* it returns (and that is correct):
      reportForm:customers:0:fullName
      In ver. 3.2.* it returns (incorrect):
      reportForm:customers:fullName

      Please explain how to fix these issues.

      Thanks,

      Igor

        • 1. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
          igor_d

          I meant:
          input.getClientId(facesContext)
          In ver. 3.1.* returns:
          reportForm:customers:0:fullName
          In ver. 3.2.* returns:
          reportForm:customers:fullName

          • 2. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
            ilya_shaikovsky

            already found this and fixed in latest 3.2.2 snapshots.

            • 3. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
              igor_d

              Hi Ilya,

              We have tested the issues with 3.2.2.CR4.
              Below is my explanation:

              If you have in a form:

               <a4j:repeat id="customers" value="#{customers}" var="party">
              
               <rich:panel id="customerDetailsPanel" header="PART A>
              
               <h:inputText id="fullName"
               required="true"
               requiredMessage="party.mainName.isNull"
               value="#{party.nameMainNameText}"/>
              


              We have a PhaseListener, which adds to the input component our Validator, which validates the input.
              The issues are:
              1) inputComponent.getSubmittedValue() still returns null, which is different to 3.1.* (this is not fixed yet)

              2) in our validator in the standard interface method
              public void validate(final FacesContext facesContext, final UIComponent component, final Object value) throws ValidatorException {

              if value is invalid we add message:
              facesContext.addMessage(input.getClientId(facesContext), facesMessage)


              In that place
              input.getClientId(facesContext)
              returns the correct value:
              reportForm:customers:0:fullName
              (this is fixed, thanks!)

              2) In another place (PhaseListener:
              public void afterPhase(final PhaseEvent e) {

              we call input.getClientId(facesContext), and that returns:
              reportForm:customers:fullName
              And that is different to 3.1.* (this is not fixed yet)

              3) Later on we want to see which panel this input control belongs to. We call panelComponent.getClientId(context).
              That also returns incorrect value:
              reportForm:customers:customerDetailsPanel
              not what we expect:
              reportForm:customers:0:customerDetailsPanel
              this is not fixed, too

              Can you please have another look. It looks like the getClientId(facesContext) problem is fixed in one place, but it's not in some other places.

              Best regards,
              Igor

              • 4. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                nbelaevski

                 

                "igor_d" wrote:
                2) In another place (PhaseListener:
                public void afterPhase(final PhaseEvent e) {

                we call input.getClientId(facesContext), and that returns:
                reportForm:customers:fullName
                And that is different to 3.1.* (this is not fixed yet)

                3) Later on we want to see which panel this input control belongs to. We call panelComponent.getClientId(context).
                That also returns incorrect value:
                reportForm:customers:customerDetailsPanel
                not what we expect:
                reportForm:customers:0:customerDetailsPanel
                this is not fixed, too

                Can you please have another look. It looks like the getClientId(facesContext) problem is fixed in one place, but it's not in some other places.

                Best regards,
                Igor


                Igor,

                It is ok that there is no row index is appended to client id when client id is calculated inside phase listener. Probably 3.1.x contains a bug that causes wrong calculation of client id.

                • 5. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                  igor_d

                  Hi Nick,

                  well, I don't think it's ok, because if we have several iterations of customers, then how can we define which one which, if client id is the same for all.
                  Also, we don't use (see problem #3) panel.getClientId() in PhaseListener, but in a controller to render invalid messages with link to their input controls, which are located in particular panels (so, we use java script onclick: 1. make all panels hidden, 2. make the panel with invalid value visible, 3. go to the invalid input control).
                  We have several panels with only 1 visible, which is selected by a user. So we need to know the client id of the panel to make it visible.

                  Another thing is, why would it be correct in Validator.validate method but not everywhere else. Should not we make it consistent?

                  Thanks again,
                  Igor

                  • 6. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                    nbelaevski

                    Igor,

                    Client id is built dynamically for components inside iterables like dataTable, repeat, etc. It contains current row identifier when:

                    1. In the context of iterable component being processed (e.g. decoded, validated, encoded etc.)
                    2. When events queued from component inside iterable component are broadcast (e.g. command link inside data table)
                    3. In the context of invokeOnComponent() method

                    Can you provide full page code or little test case application to find out what's really going on?

                    • 7. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                      igor_d

                      Hi Nick,

                      here is a method, which we use to define if the value was entered into the "required" field:

                       public void afterPhase(final PhaseEvent e) {
                       final FacesContext fc = FacesContext.getCurrentInstance();
                       final List<UIInput> components = new ArrayList<UIInput>();
                       JSFUtil.getUIComponentsByType(fc, UIInput.class, components);
                       for (final UIInput inputComponent : components) {
                       // if input was required, check if submitted value is not empty
                       if (Boolean.TRUE.equals(inputComponent.getAttributes().get(INPUT_REQUIRED))) {
                       OurJsfValidator.validateRequired(fc, inputComponent);
                       }
                       }
                       }
                      


                      What we want to achive is to be able to submit form with invalid data (save "dirty data" into xml field), so a user can work on them later.
                      For that we used attribute inputRequired:
                      <h:inputText value="#{controller.someVar}" inputRequired="true"/>
                      That will not break the standard JSF validation with required="true". But we still want to check the value.

                      Here is the static method in OurJsfValidator:

                       public static void validateRequired(final FacesContext facesContext, final UIInput input) {
                       if (Boolean.TRUE.equals(input.getAttributes().get(EMPTY_VALUE))) {
                       String message = input.getRequiredMessage();
                       if (!StringUtils.isEmpty(message)) {
                       message = message.indexOf("{") == 0 && message.indexOf("}") == message.length() - 1 ? SeamResourceBundle
                       .getBundle().getString(message.substring(1, message.length() - 1))
                       : message;
                       } else {
                       message = MESSAGE_INPUT_REQUIRED;
                       }
                      
                      facesContext.addMessage(input.getClientId(facesContext), FacesMessages.createFacesMessage(
                       FacesMessage.SEVERITY_ERROR, message));
                       }
                       }
                      

                      As you can see we want to create a faces message and assign it to the correct input field. But since the client id is not calculated correctly we have incorrect links for the validation messages.

                      The attribute EMPTY_VALUE is set to 'true' before validation, and in validation we set it to 'false' if there was some value. But if there is no value JSF does not call our validator, so we have to use this attribute.

                      So, this is the problem, which appears to be in ver 3.2.2.GA. That all works fine in 3.1.*

                      Thanks,
                      Igor

                      • 8. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                        igor_d

                        From http://java.sun.com/javaee/5/docs/api/:

                        public abstract String getClientId(FacesContext context)Return a client-side identifier for this component, generating one if necessary. The associated Renderer, if any, will be asked to convert the clientId to a form suitable for transmission to the client.

                        The return from this method must be the same value throughout the lifetime of the instance, unless the id property of the component is changed, or the component is placed in a NamingContainer whose client ID changes (for example, UIData). However, even in these cases, consecutive calls to this method must always return the same value. The implementation must follow these steps in determining the clientId:

                        Find the closest ancestor to this component in the view hierarchy that implements NamingContainer. Call getContainerClientId() on it and save the result as the parentId local variable. Call getId() on this component and save the result as the myId local variable. If myId is null, call context.getViewRoot().createUniqueId() and assign the result to myId. If parentId is non-null, let myId equal parentId + NamingContainer.SEPARATOR_CHAR + myId. Call Renderer.convertClientId(javax.faces.context.FacesContext, java.lang.String), passing myId, and return the result.


                        Parameters:
                        context - The FacesContext for the current request
                        Throws:
                        NullPointerException - if context is null

                        • 9. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                          nbelaevski

                          a4j:repeat is an example of

                          "igor_d" wrote:
                          NamingContainer whose client ID changes (for example, UIData)


                          • 10. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                            igor_d

                            Right, so there is no solution? Good :-(
                            Need a workaround then.

                            • 11. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                              abelevich

                              Try this code:


                              final FacesContext context = FacesContext.getCurrentInstance();

                              //Find UIRepeat componet before

                              if (component != null && (component instanceof UIRepeat)) {
                              final UIRepeat repeater = (UIRepeat)component;
                              Object rowKey = repeater.getRowKey();
                              repeater.captureOrigValue();

                              try {
                              DataVisitor visitor = new DataVisitor() {

                              public void process(FacesContext context, Object rowKey, Object argument) throws IOException {
                              repeater.setRowKey(rowKey);
                              ListIterator childIterator = repeater.getChildren().listIterator();

                              while (childIterator.hasNext()) {
                              UIComponent child = (UIComponent) childIterator.next();
                              if (Boolean.TRUE.equals(child.getAttributes().get(INPUT_REQUIRED))) {
                              OurJsfValidator.validateRequired(context, child);
                              }

                              }
                              }

                              };

                              repeater.walk(context, visitor, null);
                              } catch (IOException exception){
                              }finally{
                              repeater.restoreOrigValue(context);
                              repeater.setRowKey(rowKey);
                              }
                              }


                              • 12. Re: Incorrect getClientId and getSubmittedvalue in Richfaces
                                igor_d

                                Anton, thanks for your help - really appreciate it! Good stuff!