1 2 3 Previous Next 31 Replies Latest reply on Nov 25, 2007 7:48 PM by kragoth

    selectItems - Is it possible?

    kragoth

      Hi all.
      Before I get into my question, I have been trying for 2 days to achieve what I want to do and I've read almost every article I can find about the s:selectItems and f:selectItems components that I can find. But, I can't seem to find any examples or I am just not understanding things which is why I can't get this to work. But anyways, this is my problem.

      I have a web page that I want to use the selectOneMenu component on to select a "Category" Object.

      So I figured I could just use the following code:

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html 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"
       xmlns:rich="http://richfaces.org/rich"
       xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
       xmlns:s="http://jboss.com/products/seam/taglib"
       xmlns:j4j="http://javascript4jsf.dev.java.net/"
       xmlns:gekko="http://gekkoTagLibs.com">
      
       <head></head>
       <body>
       <h:form id="searchCriteria" >
       <rich:panel>
       <f:facet name="header">
       <h:outputText value="Factor for Category/Subcategory Enquiry (TLONL999)"></h:outputText>
       </f:facet>
      
       <rich:panel>
       <h:messages globalOnly="true"/>
       </rich:panel>
      
       <h:panelGrid columns="3">
       <h:outputText value="Category:" styleClass="inputLabel"></h:outputText>
       <h:selectOneMenu id="categoryFilter" value="#{catSubcatEnquiryActionBean.categoryCode}" >
       <s:selectItems value="#{categories}" var="category" noSelectionLabel=""
       label="#{category.name}"/>
       <f:attribute name="className" value="CategoryCode"/>
       </h:selectOneMenu>
       <h:message for="categoryFilter"></h:message>
       <h:outputText value="Subcategory:" styleClass="inputLabel"></h:outputText>
       <h:selectOneListbox id="subcategoryFilter">
       </h:selectOneListbox>
       <h:message for="subcategoryFilter"></h:message>
       <h:outputText value="Date:" styleClass="inputLabel"></h:outputText>
       <rich:calendar id="calendarFilter"
       showInput="true"
       enableManualInput="true"
       datePattern="#{catSubcatEnquiryActionBean.dateFormat}"
       currentDate="#{catSubcatEnquiryActionBean.currentDate}"
       value="#{catSubcatEnquiryActionBean.dateFilter}"
       converter="gekkoDateConverter"
       >
       </rich:calendar>
       <h:message id="calendarErrors" for="calendarFilter"></h:message>
       </h:panelGrid>
      
       </rich:panel>
      
       <h:commandButton id="formSubmit" type="submit" value="Search" action="#{catSubcatEnquiryActionBean.doSearch}">
       <gekko:defaultAction/>
       </h:commandButton>
      
       </h:form>
      
       </body>
      </html>
      


      the CatSubcatEnquiryActionBean is as follows:
      package gekko.web.actionbean.enquiryservices;
      
      import gekko.domain.reference.ltl.CategoryCode;
      import gekko.services.query.Query;
      import gekko.services.query.QueryService;
      import gekko.services.reference.DateService;
      import gekko.util.FormatUtils;
      import gekko.util.StringUtils;
      
      import java.util.Date;
      import java.util.List;
      
      import javax.faces.application.FacesMessage;
      import javax.faces.context.FacesContext;
      import javax.faces.validator.ValidatorException;
      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Factory;
      import org.jboss.seam.annotations.In;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      import org.jboss.seam.annotations.datamodel.DataModel;
      import org.jboss.seam.annotations.datamodel.DataModelSelection;
      import org.jboss.seam.faces.FacesMessages;
      
      @Name("catSubcatEnquiryActionBean")
      @Scope(ScopeType.SESSION)
      public class CatSubcatEnquiryActionBean {
      
       @In("#{dateServiceImpl}")
       private DateService dateSvc; //NOPMD
      
       @In("#{queryServiceImpl}")
       private QueryService querySvc;
      
       @In
       FacesMessages facesMessages;
      
       private Date dateFilter;
       private String subcategory;
      
       @DataModel("categories")
       private List<CategoryCode> categoryList; //NOPMD
      
       @DataModelSelection("categories")
       private CategoryCode categoryCode;
      
       @Factory(value="categories")
       public void getCategories() {
       Query<CategoryCode> qCat = querySvc.createQuery(CategoryCode.class);
       qCat.equalsConstraint("deleted", false);
       categoryList = querySvc.list(qCat);
       }
      
       public Date getDateFilter() {
       return dateFilter;
       }
      
       public void setDateFilter(Date dateFilter) {
       this.dateFilter = dateFilter;
       }
      
       public Date getCurrentDate() {
       return dateSvc.getCurrentDate();
       }
      
       public String getDateFormat() {
       return FormatUtils.SHORT_DATE_FORMAT;
       }
      
       public String getSubcategory() {
       return subcategory;
       }
      
       public void setSubcategory(String subcategory) {
       this.subcategory = subcategory;
       }
      
       public List<CategoryCode> getCategoryList() {
       return categoryList;
       }
      
       public void setCategoryList(List<CategoryCode> categoryList) {
       this.categoryList = categoryList;
       }
      
       public CategoryCode getCategoryCode() {
       return categoryCode;
       }
      
       public void setCategoryCode(CategoryCode categoryCode) {
       this.categoryCode = categoryCode;
       }
      
       public String doSearch() throws ValidatorException {
      
       if (StringUtils.isBlank(this.subcategory) &&
       StringUtils.isBlank(FormatUtils.formatDate(this.dateFilter, FormatUtils.SHORT_DATE_FORMAT, ""))) {
      
       FacesMessage message = new FacesMessage();
       message.setDetail("At least one of Category or Date must be filled in to performa a search.");
       message.setSummary("No search criteria given");
       message.setSeverity(FacesMessage.SEVERITY_ERROR);
      
       FacesContext.getCurrentInstance().addMessage(null, message);
      
       }
      
       return "";
       }
      
      }
      



      However, when I use this. I get the following message when I select a Category and submit the form.

      /web/enquiryServices/categorySubcategory/catSubcategoryEnquiry.xhtml @26,95 value="#{catSubcatEnquiryActionBean.categoryCode}": java.lang.IllegalArgumentException: argument type mismatch
      


      I've attempted to use the <s:convertEntity/> but this gives me a No Transaction error and would not work anyway because the CatSubcatEnquiryActionBean is not an Entity.

      I can't make the Category.java a Seam bean due to the way our system architect wants to seperate the persistence from Seam. They only want Seam for its JSF integration.

      Just in case this is needed to solve my problem here is my faces-config.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
      
       <!--<component>-->
       <!--<component-type>gekko.web.defaultAction</component-type>-->
       <!--<component-class>-->
       <!--gekko.web.components.UIDefaultAction-->
       <!--</component-class>-->
       <!--</component>-->
      
       <!-- Facelets support -->
       <application>
       <variable-resolver>
       org.springframework.web.jsf.DelegatingVariableResolver
       </variable-resolver>
       <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
       <message-bundle>messages</message-bundle>
       </application>
      
       <converter>
       <converter-id>gekkoDateConverter</converter-id>
       <converter-class>
       gekko.web.converter.DateConverter
       </converter-class>
       </converter>
      
       <converter>
       <converter-id>gekkoGenericObjectConverter</converter-id>
       <converter-class>
       gekko.web.converter.GenericObjectConverter
       </converter-class>
       </converter>
      
      </faces-config>
      


      and my components.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://jboss.com/products/seam/components"
       xmlns:core="http://jboss.com/products/seam/core"
       xmlns:bpm="http://jboss.com/products/seam/bpm"
       xmlns:security="http://jboss.com/products/seam/security"
       xmlns:framework="http://jboss.com/products/seam/framework"
       xmlns:transaction="http://jboss.com/products/seam/transaction"
       xmlns:spring="http://jboss.com/products/seam/spring"
       xsi:schemaLocation=
       "http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd
       http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
       http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.0.xsd
       http://jboss.com/products/seam/persistence http://jboss.com/products/seam/transaction-2.0.xsd
       http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
       http://jboss.com/products/seam/framework http://jboss.com/products/seam/framework-2.0.xsd
       http://jboss.com/products/seam/spring http://jboss.com/products/seam/spring-2.0.xsd">
      
      
       <core:init transaction-management-enabled="false">
       </core:init>
       <transaction:no-transaction/>
      
      </components>
      



      I guess I just want to know. How do I select an item in my selectOneMenu and update my CatSubCatEnquiryActionBean?

      I thought this would be easy, but I'm really struggeling to find a way to do this.

      Do I need to write my own converter? And if so, how do I find out which selectItem was the selected one?

      I hope someone can make sense of what I'm trying to do cause I'm a tad confused at the moment.

      Thanks






        • 1. Re: selectItems - Is it possible?
          dhinojosa

          1. Is there more to your stack trace?
          2. What version are you using?
          3. Your bean is not stateful, intentional?



          • 2. Re: selectItems - Is it possible?
            kragoth

            1
            No stack trace is given. The error message is from a h:messages component.

            2
            Seam 2.0.0CR2
            JSF 1.2

            3
            I'm not entirely sure what I need to do regards Stateful / Stateless for this bean.

            Another slightly smaller issue is that it does not select the "no Selection" item as default. I would like this to be the case. Any ideas on that?


            I have created a somewhat working version by using the following.

            The xhtml file:

            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
            <html 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"
             xmlns:rich="http://richfaces.org/rich"
             xmlns:a4j="https://ajax4jsf.dev.java.net/ajax"
             xmlns:s="http://jboss.com/products/seam/taglib"
             xmlns:j4j="http://javascript4jsf.dev.java.net/"
             xmlns:gekko="http://gekkoTagLibs.com">
            
             <head></head>
             <body>
             <h:form id="searchCriteria" >
             <rich:panel>
             <f:facet name="header">
             <h:outputText value="Factor for Category/Subcategory Enquiry (TLONL999)"></h:outputText>
             </f:facet>
            
             <rich:panel>
             <h:messages globalOnly="true"/>
             </rich:panel>
            
             <h:panelGrid columns="3">
             <h:outputText value="Category:" styleClass="inputLabel"></h:outputText>
             <h:selectOneMenu id="categoryFilter" value="#{catSubcatEnquiryActionBean.categoryCode}" converter="gekkoGenericObjectConverter">
             <s:selectItems value="#{categories}" var="category" noSelectionLabel="none"
             label="#{category.name}"/>
             </h:selectOneMenu>
             <h:message for="categoryFilter"></h:message>
             <h:outputText value="Subcategory:" styleClass="inputLabel"></h:outputText>
             <h:selectOneListbox id="subcategoryFilter">
             </h:selectOneListbox>
             <h:message for="subcategoryFilter"></h:message>
             <h:outputText value="Date:" styleClass="inputLabel"></h:outputText>
             <rich:calendar id="calendarFilter"
             showInput="true"
             enableManualInput="true"
             datePattern="#{catSubcatEnquiryActionBean.dateFormat}"
             currentDate="#{catSubcatEnquiryActionBean.currentDate}"
             value="#{catSubcatEnquiryActionBean.dateFilter}"
             converter="gekkoDateConverter"
             >
             </rich:calendar>
             <h:message id="calendarErrors" for="calendarFilter"></h:message>
             </h:panelGrid>
            
             </rich:panel>
            
             <h:commandButton id="formSubmit" type="submit" value="Search" action="#{catSubcatEnquiryActionBean.doSearch}">
             <gekko:defaultAction/>
             </h:commandButton>
            
             </h:form>
            
             </body>
            </html>
            



            The converter
            package gekko.web.converter;
            
            import gekko.domain.PersistentEntity;
            import gekko.domain.reference.ltl.CategoryCode;
            
            import javax.faces.component.UIComponent;
            import javax.faces.context.FacesContext;
            import javax.faces.convert.Converter;
            import javax.faces.convert.ConverterException;
            
            import com.sun.faces.util.MessageFactory;
            
            import org.jboss.seam.jsf.ListDataModel;
            
            public class GenericObjectConverter implements Converter {
            
             /**
             * <p>The standard converter id for this converter.</p>
             */
             public static final String CONVERTER_ID = "gekko.web.converter.GenericObjectConverter";
            
             /**
             * <p>The message identifier of the {@link javax.faces.application.FacesMessage} to be created if
             * the conversion to <code>Date</code> fails. The message format
             * string for this message may optionally include the following
             * placeholders:
             * <ul>
             * <li><code>{0}</code> replaced by a <code>String</code> whose value
             * is the label of the input component that produced this message.</li>
             * </ul></p>
             */
             public static final String NO_CLASS_SPECIFIED =
             "gekko.web.converter.GenericObjectConverter.NOCLASS";
            
             /**
             * <p>The message identifier of the {@link javax.faces.application.FacesMessage} to be created if
             * the conversion of the <code>DateTime</code> value to
             * <code>String</code> fails. The message format string for this message
             * may optionally include the following placeholders:
             * <ul>
             * <li><code>{0}</code> replaced by a <code>String</code> whose value
             * is the label of the input component that produced this message.</li>
             * </ul></p>
             */
             public static final String INVALID_CLASS =
             "gekko.web.converter.GenericObjectConverter.INVALIDCLASS";
            
             @Override
             public Object getAsObject(FacesContext context, UIComponent component,
             String value) {
            
             try {
            
             for(UIComponent comp : component.getChildren()) {
            
             ListDataModel objectList = (ListDataModel)comp.getValueExpression("value").getValue(context.getELContext());
             objectList.setRowIndex(0);
             while (objectList.isRowAvailable()) {
             CategoryCode currentCategory = (CategoryCode)objectList.getRowData();
             if (currentCategory.getId().toString().equals(value)) {
             return currentCategory;
             }
             objectList.setRowIndex(objectList.getRowIndex() + 1);
             }
             }
             } catch (ConverterException e) { // NOPMD
             throw e;
             } catch (Exception e) {
             throw new ConverterException(e);
             }
            
             return null;
             }
            
             @Override
             public String getAsString(FacesContext context, UIComponent component,
             Object value) {
            
             String id;
            
             try {
            
             if (value == null) {
             return "";
             }
            
             id = ((PersistentEntity)value).getId().toString();
             return id;
            
             } catch (ConverterException e) {
             throw new ConverterException(MessageFactory.getMessage(
             context, INVALID_CLASS,
             MessageFactory.getLabel(context, component)), e);
             } catch (Exception e) {
             throw new ConverterException(MessageFactory.getMessage(
             context, INVALID_CLASS,
             MessageFactory.getLabel(context, component)), e);
             }
             }
            
            }
            




            This actually works and updates my bean etc etc... so I will use this if there are no better solutions. I will look into this Stateful idea and see what affect that has.

            But I would REALLY like it if I could make the noSelectionLabel the default selection in the selectOneMenu


            • 3. Re: selectItems - Is it possible?
              kragoth

              Ok, I've made progress!!!

              If I remove the @DataModelSelection annotation everything works exactly the way I want it.

              Hopefully someone else gets some good out of this code!

              Infact maybe the Seam guys should implement a generic converter for non Entity classes.

              • 4. Re: selectItems - Is it possible?
                pmuir

                 

                "Kragoth" wrote:
                Infact maybe the Seam guys should implement a generic converter for non Entity classes.


                How do you propose we do that?

                • 5. Re: selectItems - Is it possible?

                  A page-scoped map of converter-generated GUID -> object references? Seems relatively easy on the surface; pretty much every UI framework I've worked with has support for doing that (using client-side or server-side state saving). Though it must not be that easy with JSF, because otherwise I'm sure you would have done that by now :/

                  • 6. Re: selectItems - Is it possible?
                    pmuir

                    Feel like writing a patch? :)

                    • 7. Re: selectItems - Is it possible?

                      It'd be a new converter and not a patch, right? I'm still hesitant, since if it really is as easy as it seems then it makes no sense why you'd have gone through all the trouble of writing the Entity converter...

                      • 8. Re: selectItems - Is it possible?
                        dhinojosa

                        I vote -1 on that, unless my mind can be changed on it, but it seems that everything needed can either be an an enum, an entity, or a String. I just don't see regular undefined pojos needed in a select case.

                        • 9. Re: selectItems - Is it possible?
                          dhinojosa

                          Kragoth,

                          Stateful beans hold state. Stateless will not hold, or guarantee to hold member variable data when you leave it. So if you are selecting data from a bean and hope to come back to that same instance it needs to be stateful.

                          But now you forced me to ask a question. ;) Gonna make it a trivia question

                          • 10. Re: selectItems - Is it possible?

                            What if you want a datagrid with info from entity fields along with checkboxes for some UI task? You obviously don't want to add a boolean field to the entity class for a purely UI feature, so you'll need to create a POJO wrapping the entity and adding the boolean field...

                            • 11. Re: selectItems - Is it possible?
                              dhinojosa

                              Forgot to specify that the stateful is intended for EJB3 database stateful conversation. So if you don't use EJB3 (which it looks like you don't) you will not need it. (Verification would be good from others).

                              • 12. Re: selectItems - Is it possible?
                                pmuir

                                1) I never came across a use case in the wild that, as Dan says, wasn't either an entity or supported by a built in JSF converter (your example is covered by javax.faces.Boolean)

                                2) You can't convert entities like that as JSF may serialize page scoped variables (when using client-side state saving which we used to *have* to use with MyFaces and Seam) which causes them to become unmanaged (which is *bad*).

                                3) Well I/we use the term patch to cover any modification to the code base, whether it involves adding new classes or not. I find diff format much easier to read than some random zip with classes in it.

                                • 13. Re: selectItems - Is it possible?
                                  pmuir

                                  oops. Of course, you mean with a boolean field added. Well in this case my (2) applies. So there is no easy way to write a generic converter in this case short of caching the objects in session scope.

                                  But, I'm open to ideas (I did the initial work on this for Seam 1.1 or something and the usage hasn't changed much since, even if the implementation has) - perhaps we can improve on it now?

                                  • 14. Re: selectItems - Is it possible?

                                    Ok, #2 would definitely explain why there's an EntityConverter and not an ObjectConverter.

                                    It seems there are, at the very least, three ways to approach this:
                                    1. Do what I originally suggested (an "ObjectConverter" that serializes to the page scope), requiring objects to not be (or contain!) entity classes

                                    2. Like #1, except instead requiring server-side state saving to be enabled (the default for Sun's JSF implementation it seems).

                                    3. Like #1, except using the session scope and thus removing the limitations. Are there any drawbacks to caching in the session scope instead of the page scope?

                                    What're your thoughts? Any other options I'm missing?

                                    1 2 3 Previous Next