14 Replies Latest reply on Oct 22, 2005 1:46 PM by patrick_ibg

    JSF, Facelets, selectOneMenu and Converter issue (Long)

    lcoetzee

      Hi all,

      I assume this is more appropriate to some of the JSF forums but unfortunately Google has not been very helpful.

      I have a EJB3 Entity bean (Position) containing an id (Long) and a description (String). I want to use these Position entities as lookups in my Seam/JSF/Facelets application.

      In order to do this I have to make use of a Converter to translate to and from the view and action handler class. I have written such a converter (extending Converter) and have registered it in my faces-config.xml


      <converter>
       <converter-id>
       PositionConv
       </converter-id>
       <converter-class>
       csir.league.seam.PositionConverter
       </converter-class>
      </converter>
      


      Similarly I register the use of my Converter in my selectOneMenu.

      <h:selectOneMenu value="#{chosenPosition}" converter="PositionConv">
       <f:selectItems value="#{allPositions}" />
      </h:selectOneMenu>
      


      allPositions and chosenPosition are defined in my ActionHandler.
      @Out(required=false)
      private List<SelectItem> allPositions;
      
      @Out(required=false)
      private Position chosenPosition;
      


      allPositions are populated with SelectItem elements containing references to my Position objects.

      allPositions = new ArrayList<SelectItem>();
      for (Iterator iter = trySeam1Entity.createQuery("from csir.league.seam.Position").getResultList().iterator(); iter.hasNext();)
      {
       Position position = (Position) iter.next();
       SelectItem s = new SelectItem(position,position.getPositionName(),position.getPositionName());
       allPositions.add(s);
      }
      


      chosenPosition is initiated to a default value.

      During rendering of my page the select is populated with the correct objects and displays the right Strings.

      The problem is unfortunately upon submit where the chosenPosition never gets set to the selected item.

      In my Converter I can see that it receives the correctly selected item, but nothing useful from there on.

      Any suggestions ?


      Thanks

      Louis

        • 1. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)

          Have you tried using a composite? I would have to dive into Seam source and where the variables were being dropped, but 99% of the time, form variables are bound to properties #{foo.property}, not #{property}.

          That might be the issue.

          • 2. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
            lcoetzee

            My actionbean (Seam component) is defined with the following tags

            Stateful
            @Name("leaguemanagement")
            @Scope(ScopeType.CONVERSATION)
            @Conversational(ifNotBegunOutcome="fail")
            @Interceptor(SeamInterceptor.class)
            


            In the bean I have the two properties defined as follows

            @Out(required=false)
             private List<SelectItem> allPositions;
            @Out(required=false)
             private Position chosenPosition;
            


            If I use the composite as suggested
            <h:form>
             <h:selectOneMenu value="#{leaguemanagement.chosenPosition}" converter="PositionConv">
             <f:selectItems value="#{leaguemanagement.allPositions}" />
             </h:selectOneMenu>
             <h:commandButton value="Save Team" action="#{leaguemanagement.updateTeam}"/>
             <h:commandButton value="Return to main" action="#{leaguemanagement.cancel}"/>
             </h:form>
            


            I get the following exceptions (selected parts of stacktrace):

            10:00:52,322 ERROR [PropertyResolverImpl] $Proxy161
            javax.faces.el.PropertyNotFoundException: Bean: $Proxy161, property: allPositions
             at org.apache.myfaces.el.PropertyResolverImpl.getPropertyDescriptor(PropertyResolverImpl.java:428)
             at
            .
            .
            .
            10:00:52,332 INFO [STDOUT] 2005/10/05 10:00:52 com.sun.facelets.FaceletViewHandler handleRenderException
            SEVERE: Error Rendering View
            javax.faces.el.PropertyNotFoundException: /secure/league/player.xhtml @24,64 value="#{leaguemanagement.allPositions}": Bean: $Proxy161, property: allPositions
             at com.sun.facelets.el.LegacyValueBinding.getValue(LegacyValueBinding.java:58)
            .
            .
            .
            10:00:52,333 INFO [STDOUT] 2005/10/05 10:00:52 com.sun.facelets.FaceletViewHandler handleRenderException
            SEVERE: Took Type: org.apache.catalina.connector.CoyoteWriter
            10:00:52,557 ERROR [PropertyResolverImpl] $Proxy161
            javax.faces.el.PropertyNotFoundException: Bean: $Proxy161, property: chosenPosition
            .
            .
            .
            10:00:52,558 ERROR [PropertyResolverImpl] $Proxy161
            javax.faces.el.PropertyNotFoundException: Bean: $Proxy161, property: allPositions
             at org.apache.myfaces.el.PropertyResolverImpl.getPropertyDescriptor(PropertyResolverImpl.java:428)
             .
            .
            .
            
            



            Using the properties directly I am able to show the view with the content, but the property chosenPosition are not being updated on Submit
            <h:form>
             <h:selectOneMenu value="#{chosenPosition}" converter="PositionConv">
             <f:selectItems value="#{allPositions}" />
             </h:selectOneMenu>
             <h:commandButton value="Save Team" action="#{leaguemanagement.updateTeam}"/>
             <h:commandButton value="Return to main" action="#{leaguemanagement.cancel}"/>
             </h:form>
            


            The Position Entity is defined as follows:
            
            @Entity
            @Name("position")
            @Scope(ScopeType.SESSION)
            @Table(name = "position")
            @Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
            public class Position implements Serializable {


            Any suggestions ?

            Thanks

            Louis


            • 3. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
              lcoetzee

              Solved !

              My problem was that I did not define

              List<SelectItem> getAllPositions();
              void setAllPositions(List<SelectItem> allPositions);
              Position getChosenPosition();
              void setChosenPosition(Position position);

              in my ActionBean interface even though it was implemented in my ActionBean. Once I did it, I was able to access (get and set) it properly from my xhtml using the composite approach as suggested earlier !

              <h:form>
              <h:selectOneMenu value="#{leaguemanagement.position}" converter="PositionConv">
              <f:selectItems value="#{leaguemanagement.allPositions}" />
              </h:selectOneMenu>
              <h:commandButton value="Save Team" action="#{leaguemanagement.updateTeam}"/>
              <h:commandButton value="Return to main" action="#{leaguemanagement.cancel}"/>
              </h:form>


              Slowly by surely starting to win ;-) Did a few weird things with my Converter to be able to get hold of the allPositions list though.

              Thanks

              Louis



              • 4. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)

                Hey, that's great news! Please take a look at providing any additional information back to Seam or Facelets on your successes :-)

                • 5. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                  lcoetzee

                  Would like to do so. What would be a good mechansim ? Another mail containing the solution on this forum, or maybe somewhere on a Wiki ? (Does Seam have its own Wiki yet?)

                  Louis

                  • 6. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                    patrick_ibg

                    Louis,

                    I'm trying to create my own converter that's backed by an entity class. One thing I don't quite get, on the getAsObject method, is it necessary to return an actual entity object (either detached or persistent), or is it enough to do:

                    Object getAsObject (..., String value)
                    {
                    State obj = new State () ;
                    obj.setId ("AK") ;
                    return obj ;
                    }

                    I'm having a hell of a time figuring out how I could instantiate a DAO to do an actual lookup on the persistence layer given the passed 'value'.

                    • 7. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                      patrick_ibg

                      correction... the code should read:
                      obj.setId (value) ;

                      • 8. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                        lcoetzee

                        This a summary of how I got it working:

                        For my converter to work I added a HashMap containing my Position objects to the conversation context (done in my ActionBean)

                        private void initializePositionsLookup() {
                         allPositions = new ArrayList<SelectItem>();
                         allPositionsMapped = new HashMap<String,Position>();
                         for (Iterator iter = trySeam1Entity.createQuery("from csir.league.seam.Position").getResultList().iterator(); iter.hasNext();) {
                         Position p = (Position) iter.next();
                         SelectItem s = new SelectItem(p,p.getPositionName(),p.getPositionName());
                         allPositions.add(s);
                         allPositionsMapped.put(p.getPositionName(),p);
                         }
                         //these are non-seam components that we add to Seam contexts so that we can get hold of it
                         //in the PositionConverter
                         Contexts.getConversationContext().set("allPositionsMapped",allPositionsMapped);
                         logger.info("Retrieved all " + allPositions.size() + " positions ");
                         }
                        


                        In my converter I get hold of the Position objects using the above HashMap
                        package csir.league.seam;
                        
                        import java.util.Map;
                        
                        import javax.faces.component.UIComponent;
                        import javax.faces.context.FacesContext;
                        import javax.faces.convert.Converter;
                        import javax.faces.convert.ConverterException;
                        
                        import org.apache.log4j.Logger;
                        import org.jboss.seam.contexts.Context;
                        import org.jboss.seam.contexts.Contexts;
                        
                        public class PositionConverter implements Converter {
                         protected Logger logger = Logger.getLogger(PositionConverter.class);
                        
                         public PositionConverter() {
                         }
                         public Object getAsObject(FacesContext fc, UIComponent arg1, String arg2)
                         throws ConverterException {
                         if (null == arg2) {
                         logger.info("getAsObject has null arg2");
                         return null;
                         }
                         if (arg2.length() == 0) {
                         logger.info("getAsObject has zero arg2 length");
                         return null;
                         }
                         logger.info("getAsObject received " + arg2);
                         Context ctx = Contexts.getConversationContext();
                         for (String s: ctx.getNames()) {
                         logger.info("Names in context " + s);
                         }
                         Map<String,Position> map = (Map<String,Position>)ctx.get("allPositionsMapped");
                         for (Position p : map.values()) {
                         logger.info("We have " + p.toString());
                         }
                         Position p = map.get(arg2);
                         logger.info("Returning obj: " + p.getPositionName());
                         return p;
                         }
                        
                         public String getAsString(FacesContext arg0, UIComponent arg1, Object posObj)
                         throws ConverterException {
                         if (posObj instanceof Position) {
                         Position position = (Position) posObj;
                         logger.info("getAsString received " + position.getPositionName());
                         return position.getPositionName();
                         } else
                         return null;
                         }
                        
                        }
                        


                        This in conjunction with my xhtml:
                        <h:form>
                         <h:selectOneMenu value="#{leaguemanagement.position}" converter="PositionConv">
                         <f:selectItems value="#{leaguemanagement.allPositions}" />
                         </h:selectOneMenu>
                         <h:commandButton value="Save Team" action="#{leaguemanagement.updateTeam}"/>
                         <h:commandButton value="Return to main" action="#{leaguemanagement.cancel}"/>
                        </h:form>
                        


                        I also had to declare these methods in my ActionBean interface as well as implement them in my action bean.

                        public List<SelectItem> getAllPositions();
                        void setAllPositions(List<SelectItem> allPositions);
                        public Position getPosition();
                        void setPosition(Position position);
                        

                        the implemenation in the Action Bean:
                        private List<SelectItem> allPositions;
                        private Map<String,Position> allPositionsMapped;
                        private Position position;
                        .
                        .
                        .
                        public List<SelectItem> getAllPositions() {
                         return allPositions;
                         }
                        public void setAllPositions(List<SelectItem> allPositions) {
                         this.allPositions = allPositions;
                        }
                        public Position getPosition() {
                        logger.info("Retrieving chosenPosition: " + position.getPositionName());
                         return position;
                         }
                        public void setPosition(Position position) {
                         logger.info("Setting chosenPosition: " + position.getPositionName());
                         this.position = position;
                         }
                        


                        and finally the registration of the converter in my faces-config.xml
                         <converter>
                         <converter-id>PositionConv</converter-id>
                         <converter-class>
                         csir.league.seam.PositionConverter
                         </converter-class>
                         </converter>
                        


                        Note that I did not make use of injection to get hold of the position and allPositions attributes in my xhtml.

                        Was quite an interesting learning curve !

                        Regards

                        Louis


                        • 9. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                          patrick_ibg

                          Nice.

                          I arrived at a somewhat similar solution yesterday, posted here:

                          http://www.jboss.com/index.html?module=bb&op=viewtopic&t=71182

                          I think the main difference is that I don't have the code in an "action" bean, rather a POJO that acts as an initializer/holder for all lookup values.

                          • 10. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                            lcoetzee

                            I think your solution is way cooler !

                            Later

                            Louis

                            • 11. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                              patrick_ibg

                              This is really a question for a JSF/Facelet forum, but I haven't found any!

                              I have a project which is based on the HotelBooking app... I want to have subdirectories to hold the view pages (*.xhtml). Sounds simple, right?

                              This is my partial web.xml:

                              <web-app>
                              ...
                               <!-- MyFaces -->
                              
                               <listener>
                               <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
                               </listener>
                              
                               <context-param>
                               <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
                               <param-value>client</param-value>
                               </context-param>
                              
                               <context-param>
                               <param-name>facelets.DEVELOPMENT</param-name>
                               <param-value>true</param-value>
                               </context-param>
                              
                               <context-param>
                               <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
                               <param-value>.xhtml</param-value>
                               </context-param>
                              
                               <servlet>
                               <servlet-name>FacesServlet</servlet-name>
                               <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
                               <load-on-startup>1</load-on-startup>
                               </servlet>
                              
                               <!-- Faces Servlet Mapping -->
                               <servlet-mapping>
                               <servlet-name>FacesServlet</servlet-name>
                               <url-pattern>*.jsf</url-pattern>
                               </servlet-mapping>
                              ...
                              </web-app>
                              


                              Here's part of my faces-config:
                              ...
                               <navigation-rule>
                               <navigation-case>
                               <from-outcome>login</from-outcome>
                               <to-view-id>/home.xhtml</to-view-id>
                               <redirect/>
                               </navigation-case>
                              
                               <navigation-case>
                               <from-outcome>password</from-outcome>
                               <to-view-id>/password.xhtml</to-view-id>
                               </navigation-case>
                              
                               <navigation-case>
                               <from-outcome>main</from-outcome>
                               <to-view-id>/main.xhtml</to-view-id>
                               </navigation-case>
                              
                               <navigation-case>
                               <from-outcome>register</from-outcome>
                               <to-view-id>/register/account.xhtml</to-view-id>
                               </navigation-case>
                              
                               </navigation-rule>
                              
                              
                              
                               <!-- Facelets support -->
                              
                               <application>
                               <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
                               </application>
                               ...
                              


                              As you can see, most of the config stuff is straight out of HotelBooking... except for the last navigation case, where the to-view-id is /register/account.xhtml.

                              I've confirmed that /register/account.xhtml is present relative to the root directory of my war file. I don't think I've overlooked something stupid.

                              Anyway, linking to
                              //register/account.jsf

                              Throws a 404, resource not found error. According to the Servlet spec, a FacesServlet should map to anything that ends with jsf...

                              Any ideas?

                              • 12. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                                hoetschmann

                                What is the path to the template in your register.xhtml?
                                You have to use /template.xhtml in register.xhtml.

                                <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                                <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                                xmlns:ui="http://java.sun.com/jsf/facelets"
                                xmlns:h="http://java.sun.com/jsf/html"
                                xmlns:f="http://java.sun.com/jsf/core"
                                template="/template.xhtml">

                                And you have to modify the css path in the template.xhtml:
                                example: /mywarcontextroot/css/mycss.css.




                                Regards
                                hoetschmann

                                • 13. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                                  patrick_ibg

                                  My templates are in /templates/.xhtml,
                                  and they're referenced from register.xhtml as
                                  template="/templates/template.xhtml"

                                  I'll try playing with the template location to see what works...

                                  • 14. Re: JSF, Facelets, selectOneMenu and Converter issue (Long)
                                    patrick_ibg

                                    woops... let me repost that...

                                    The templates are in warcontextroot/templates/templatename.xhtml