10 Replies Latest reply on Jun 8, 2007 2:21 PM by Grant Rettke

    What is the common pattern for selecting objects?

    Grant Rettke Novice

      I've got a list of objects from which I need to make a selection in a menu or a radio button.

      What is the best approach to doing so?

        • 1. Re: What is the common pattern for selecting objects?
          Pete Muir Master

          Use s:convertEntity and s:selectItems as demoed in a number of examples (e.g. ui example)

          • 2. Re: What is the common pattern for selecting objects?
            Damian Harvey Master

             

            <h:selectOneMenu id="boSelector" value="#{myBean.myObject}">
             <s:selectItems value="#{myBean.myObjectList}" var="object" label="#{object.field}"/>
             <s:convertEntity />
            </h:selectOneMenu>
            



            • 3. Re: What is the common pattern for selecting objects?
              Grant Rettke Novice

              I see, thanks Pete and Damian.

              The documentation implies that you would only ever use this with JPA entity.

              Right now it looks like our system will have both JPA and non-JPA components bound to the JSF UI. As such, we don't plan to always bind entities to entities. Would this be possible?

              The documentation implies that the converter needs to know about the entity manager, does that mean that the objects that populate the control already need to have persistent IDs associated with them?

              • 4. Re: What is the common pattern for selecting objects?
                Grant Rettke Novice

                The vision is that in case we don't want to use enums to populate selection controls we could use objects instead, but those objects don't yet need to be persisted.

                • 5. Re: What is the common pattern for selecting objects?
                  Pete Muir Master

                  You can use s:selectItems with any java object, but you need to provide a JSF converter if the object isn't a string. JSF provides some (e.g. number converter), Seam adds two more (enums, already persisted JPA entities), for others, you'll need to write your own.

                  • 6. Re: What is the common pattern for selecting objects?
                    Grant Rettke Novice

                     

                    "petemuir" wrote:
                    You can use s:selectItems with any java object, but you need to provide a JSF converter if the object isn't a string. JSF provides some (e.g. number converter), Seam adds two more (enums, already persisted JPA entities), for others, you'll need to write your own.


                    Thanks Pete.

                    Pete, are there any good JSF books that you would recommend?

                    • 7. Re: What is the common pattern for selecting objects?
                      Pete Muir Master

                      Not really, the trouble is if you are using Facelets and Seam, lots of what a JSF book is about (backing beans, jsp) are irrelevant. Your best bet is Micheal (Yuan)'s Seam book. O'Reilly have a pocket guide to facelets that is well worth the $10 it costs.

                      • 8. Re: What is the common pattern for selecting objects?
                        Pete Muir Master

                        Rick Hightower's series on JSF/Facelets on IBM developer works is superb IMO

                        • 9. Re: What is the common pattern for selecting objects?
                          Grant Rettke Novice

                           

                          "petemuir" wrote:
                          Rick Hightower's series on JSF/Facelets on IBM developer works is superb IMO


                          Thanks Pete.

                          • 10. Re: What is the common pattern for selecting objects?
                            Grant Rettke Novice

                            Here is what I came up with. I am posting it in order to elicit comments on my approach (code review) and also in case this is right and anyone else needs to use selectItems with a custom converter when you want to populate it using objects. In the interest of brevity this is not a compilable

                            In other words, does this look like the typical approahc for solving a problem like this?

                            This is the object Contact from which folks can choose. This is a once off selection (for now atleast), so these are not persisted. Once one is chosen, it will become an embedded entity.

                            protected String name = "";
                            protected String title = "";
                            protected String role = "";
                            protected String phoneNumber = "";
                            protected String emailAddress = "";
                            


                            There are two components: a user facing (form-like) component that stores the users selections (this will become an entity) and then the action.
                            priority1DataUf: the "form" data, users selection goes here
                            priority1RequestAction: the action itself, it also exposes a property that is the list of contacts from which the user may choose "contacts"
                            


                            On the page itself:
                            <h:selectOneRadio id="contactSelector"
                             value="#{priority1DataUf.contact}"
                             layout="pageDirection"
                             required="true">
                             <s:selectItems value="#{priority1RequestAction.contacts}"
                             var="contact"
                             label="#{contact.title} #{contact.name}: #{contact.phoneNumber}"/>
                             <s:convertEntity/>
                             <rms:contactListConverter/>
                             </h:selectOneRadio>
                            


                            Finally, the converter. The converter assumes that it would only be used for Contacts. Contact checks for equality using NAME and TITLE. Those are the values used by the equals method on the class. The lifecycle is that the page gets loaded, and the converters gettAsString is called taking in a Contact and returning the value to populate the html control. That value roughly the key for that object in priority1RequestAction.contacts (althought it is not a map now, perhaps it ought to be). The user makes a selection on the page and submits it, and then the converter takes that key. It gets the value binding for the selectItems and tries to find an object with that key. If it finds it, that value is returned.
                            public Object getAsObject(FacesContext context,
                             UIComponent comp,
                             String value)
                             throws ConverterException {
                            
                             Object result = null;
                             try {
                             if (Contact.validItem(value)) {
                             // comp is "this" component, the one in which the tag lives.
                             // This converter may live inside, for example, a
                             // javax.faces.component.html.HtmlSelectOneRadio
                            
                             // We need to iterate through its children to get the list of Contacts
                             // from the <s:selectItems>
                             List children = comp.getChildren();
                            
                             for (Object child : children) {
                             // Its children include UIComponent(s)
                             if (child instanceof UIComponent) {
                             UIComponent c = (UIComponent) child;
                             // The value binding for a component is the data to which it is
                             // bound. An example here is:
                             // /priority1.xhtml @118,94 value="#{priority1RequestAction.contacts}":
                             // ValueExpression[#{priority1RequestAction.contacts}]
                             ValueBinding b = c.getValueBinding("value");
                             // The class of the value binding is an interface, java.util.List, since we
                             // provide java.util.List<contact>.
                             // b.getType(context) -> interface java.util.List
                             // At this point we assume it is a list of Contacts
                             List<Contact> contacts =
                             (List<Contact>) b.getValue(context);
                             for (int i = 0; i < contacts.size(); i++) {
                             Contact contact = contacts.get(i);
                             if(Contact.getItem(contact).equals(value))
                             {
                             result = contact;
                             break;
                             }
                             }
                             }
                             }
                             }
                             } catch (Exception e) {
                             throw new ConverterException(
                             "ContactListConverter error: " + e.toString());
                             }
                             if (result == null) {
                             throw new ConverterException(
                             "A valid Contact itemString is required");
                             }
                             return result;
                             }
                            
                             public String getAsString
                             (FacesContext
                             context,
                             UIComponent
                             component,
                             Object
                             object)
                             throws ConverterException {
                             String result;
                             if (object == null) {
                             result = null;
                             } else {
                             result = Contact.getItem((Contact) object);
                             }
                             return result;
                             }