4 Replies Latest reply on Oct 4, 2006 11:00 AM by smartbinary

    Problems using JSF Converters in Seam app

    smartbinary

      I'm sure this is something simple, but this is my first go around w/using Converters within a Seam app.

      -----

      I simply want to convert a name field entered in the UI to 'Firstname Lastname'. For instance, if a user enters 'Smart, Todd', I want to convert that into 'Todd Smart'. I realize there are easy ways to do this within the getter/setter for the name field ... this is an exercise to better understand how to use JSF Converters in Seam -- I eventually want to 'ajaxify' these converter sequences for more complex requirements.

      So...I created FullName and FullNameConverter classes:

      package org.jboss.seam.example.hibernate;
      
      import java.io.Serializable;
      
      public class FullName implements Serializable {
      
       private String firstName;
       private String lastName;
      
       public FullName(String unparsedName) {
       unparsedName = unparsedName.trim();
       String[] splitName = unparsedName.split(" ");
       if(splitName.length == 2 && unparsedName.indexOf(",") == -1) {
       setFirstName(splitName[0]);
       setLastName(splitName[1]);
       } else if(splitName.length == 2) {
       setFirstName(splitName[1]);
       setLastName(splitName[0].replace(",", ""));
       }
       }
      
       public String getFirstName() {
       return firstName;
       }
      
       public void setFirstName(String firstName) {
       this.firstName = firstName;
       }
      
       public String getLastName() {
       return lastName;
       }
      
       public void setLastName(String lastName) {
       this.lastName = lastName;
       }
      
       public String toString() {
       return this.firstName + " " + this.lastName;
       }
      }
      


      package org.jboss.seam.example.hibernate;
      
      import java.io.Serializable;
      
      import javax.faces.component.UIComponent;
      import javax.faces.context.FacesContext;
      import javax.faces.convert.Converter;
      import javax.faces.convert.ConverterException;
      
      import org.jboss.seam.InterceptionType;
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Intercept;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      
      @Scope(ScopeType.SESSION)
      @Name("fullNameConverter")
      @Intercept(InterceptionType.ALWAYS)
      public class FullNameConverter implements Serializable, Converter {
      
       public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
       throws ConverterException {
      
       FullName fullName;
       fullName = new FullName(arg2);
      
       return fullName;
       }
      
       public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
       throws ConverterException {
       String returnString;
      
       FullName fullName;
       fullName = (FullName) arg2;
      
       returnString = (fullName==null ? null : fullName.toString());
      
       return returnString;
       }
      
      }
      


      I'm using the AppUser class (from the Seam Hibernate3 Booking Example). Specifically, it's name property:

       @NotNull
       @Length(max=100)
       public String getName()
       {
       return name;
       }
       public void setName(String name)
       {
       this.name = name;
       }
      


      Finally, I've updated the register.xhtml from the same booking example to use the FullNameConverter for the name field:

       <div class="entry">
       <div class="label"><h:outputLabel for="name">Name (first last):</h:outputLabel></div>
       <div class="input">
       <h:inputText id="name" value="#{appUser.name}" required="true" converter="#{fullNameConverter}" >
       </h:inputText>
       <br/>
       <span class="errors"><h:message for="name" /></span>
       </div>
       </div>
      


      ...............

      When I run through the app (with my debugger attached), I find that ... when the form is submitted ... the FullNameConverter.getAsObject() is called (which does receive the inputted value). This is followed by a call to the AppUser.getName() method. The result is that the getName does NOT have the converted value. Of course, this is to be expected....as the FullNameConverter.getAsString() would need to be called to return the converted value.

      Oddly enough, when the register.xhtml file is initially executed (before the user has entered values), I show (debugger breakpoints) that FullNameConverter.getAsString() is called, followed by a call to AppUser.getName().

      Anyway...I'm sure this is something simple -- or foolish :) -- that I'm doing and/or assuming regarding using Converters within Seam. Please clue me in...


      --
      Regards,

      Todd

        • 1. Re: Problems using JSF Converters in Seam app
          pmuir

          This is how converters work :)

          The getAsString method is called for converting from the Object representation (backing bean) to a String for display on the page. The getAsObject method is called for converting from the String on the page back to an object for saving to the backing bean. So you are trying to set the name (String) as an object (FullName). This won't work.

          A nice introduction to JSF conversion:

          http://www-128.ibm.com/developerworks/java/library/j-jsf3/index.html

          • 2. Re: Problems using JSF Converters in Seam app
            denis-karpov

            As I see, it has to be like this:

            public class AppUser implements Serializable {
            ...
             private FullName name;
             public FullName getName()
             {
             return name;
             }
             public void setName(FullName name)
             {
             this.name = name;
             }
            ...
            

            then your code should work.

            • 3. Re: Problems using JSF Converters in Seam app
              smartbinary

              @petemuir: Thanks...worst part is that I was referring to that article when putting this test together :-)

              @denis-karpov: I had tried that ... can't remember why I went back ... probably issues with validation annotations and/or ORM on AppUser.

              I'll post an update once it is working.


              Thanks for the pointers,

              Todd

              • 4. Re: Problems using JSF Converters in Seam app
                smartbinary

                An update, as promised...

                Updating the bean to use the correct attribute type did indeed resolve the problem. I had started out that way, but backed off when Hibernate Validation & JPA annotations were creating a whole slew of other problems.

                So...here are the mods:

                1) Converter (just changed to be EVENT scope):

                package org.jboss.seam.example.hibernate;
                
                import java.io.Serializable;
                
                import javax.faces.component.UIComponent;
                import javax.faces.context.FacesContext;
                import javax.faces.convert.Converter;
                import javax.faces.convert.ConverterException;
                
                import org.jboss.seam.InterceptionType;
                import org.jboss.seam.ScopeType;
                import org.jboss.seam.annotations.Intercept;
                import org.jboss.seam.annotations.Name;
                import org.jboss.seam.annotations.Scope;
                
                @Scope(ScopeType.EVENT)
                @Name("fullNameConverter")
                @Intercept(InterceptionType.ALWAYS)
                public class FullNameConverter implements Serializable, Converter {
                
                 public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
                 throws ConverterException {
                
                 FullName fullName;
                 fullName = new FullName(arg2);
                
                 return fullName;
                 }
                
                 public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
                 throws ConverterException {
                 String returnString;
                
                 FullName fullName;
                 fullName = (FullName) arg2;
                
                 returnString = (fullName==null ? null : fullName.toString());
                
                 return returnString;
                 }
                
                }
                


                2) Bean (just added transient attribute for testing converter):
                .............
                
                @Entity
                @Name("appUser")
                @Scope(SESSION)
                public class AppUser implements Serializable
                {
                 private String username;
                 private String password;
                 private String name;
                 private FullName fullName;
                
                .............
                
                 @NotNull
                 @Length(max=100)
                 public String getName()
                 {
                 return name;
                 }
                 public void setName(String name)
                 {
                 this.name = name;
                 }
                
                 @Transient
                 public FullName getFullName()
                 {
                 return fullName;
                 }
                 public void setFullName(FullName fullName)
                 {
                 this.fullName = fullName;
                 }
                
                .............
                


                3) register.xhtml (now converting new field & Ajax-enabled via Ajax4jsf)
                .............
                <s:validateAll>
                
                .............
                
                <div class="entry">
                 <div class="label"><h:outputLabel for="name">Name:</h:outputLabel></div>
                 <div class="input">
                 <h:inputText id="name" value="#{appUser.name}" required="true">
                 <a4j:support event="onkeyup" reRender="nameErrors" />
                 </h:inputText>
                 <br/>
                 <a4j:outputPanel id="nameErrors">
                 <span class="errors"><h:message for="name" /></span>
                 </a4j:outputPanel>
                 </div>
                </div>
                <div class="entry">
                 <div class="label"><h:outputLabel for="fullName">Name (converted):</h:outputLabel></div>
                 <div class="input">
                 <a4j:outputPanel id="fullNamePanel">
                 <h:inputText id="fullName" value="#{appUser.fullName}" required="true" converter="#{fullNameConverter}" >
                 <a4j:support event="onchange" reRender="fullName" />
                 </h:inputText>
                 </a4j:outputPanel>
                 </div>
                </div>
                
                .............
                
                </s:validateAll>
                
                .............
                
                



                Regards,

                Todd