selectOneMenu selected value

12,827

Solution 1

A couple of notes on your code:

  1. Doing stuff in the constructor of the managed bean==bad design, use @PostConstructor instead.

  2. Manually instantiating the DAO in your converter==bad design. Ideally, your DAO should be a managed object(preferably an EJB) that you can inject into the converter. Being a managed object means it would (in theory) probably be a singleton and managed properly by the container. At least this way, you won't feel bad about it.

  3. Trying to manually manage the converter. Declare the converter as a standalone component and let the container do it's work so you can avoid unnecessary debugging.

  4. variantsMenu does not have an ajax handler attached to it so you won't get the value updated in the backing bean until the entire form is submitted

    <h:selectOneMenu id="variantsMenu"
                value="#{missionHandler.selectedVariant}"
                converter="#{variantConverter}"
                rendered="#{not empty missionHandler.selectedAircraftType}">
                <f:ajax listener="#{missionHandler.changeVariant}"       render="@form" />
                <f:selectItem itemLabel="-- Select a Variant --" />
                <f:selectItems value="#{missionHandler.variants}" var="variant"
                    itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
            </h:selectOneMenu>
    

Solution 2

The value in the managed bean is not directly updated when you select an item from the menu because the form was not submitted.

Either submit it via a <h:commandButton ...> or use an ajax event <f:ajax event="change" ...> within the menu.

Solution 3

Unless I'm missing something, I don't think you ever instantiate your converter. You bind to a bean to get the converter, but where's the value?

I can understand you're not fond about the database access in the converter. It's almost never necessary to do this if you make use of one of the select item converters in OmniFaces.

Share:
12,827
facewindu
Author by

facewindu

Updated on June 25, 2022

Comments

  • facewindu
    facewindu almost 2 years

    Well, I need some clarification on selectOneMenu tag, using EL expression to get/set values

            <h:selectOneMenu id="variantsMenu"
                value="#{missionHandler.selectedVariant}"
                converter="#{missionHandler.variantConverter}">
                <f:selectItem itemLabel="-- Select a Variant --" />
                <f:selectItems value="#{missionHandler.variants}" var="variant"
                    itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
            </h:selectOneMenu>
    

    I have this code, it displays a list containing ("-- Select a Variant--", Variant 1, Variant 2, ... Variant n) When I click on a Variant, I would expect the selectedVariant property in the missionHandler managedBean to be updated automatically. But this is not the case. The variantConverter converter is not even called to convert the String "variant.commercialName" to a real Variant object. The variant object implements hashCode() and equals()

    • What concept of EL expression / tag am I missing ?
    • What would be the solution to my update problem ?

      EDIT : managedBean (simplified)

      @ManagedBean
      @ViewScoped
      public class MissionHandler implements Serializable {
      
      private static final long serialVersionUID = 2462652101518266609L;
      
      private List<FlightFeasibilityException> exceptions;
      
      @EJB
      private VariantDao variantDao;
      
      private Variant selectedVariant;
      
      private List<Variant> variants;
      
      private VariantConverter variantConverter;
      
      public MissionHandler() {
          /** Create an empty list of exceptions */
          exceptions = new ArrayList<FlightFeasibilityException>();
      }
      
      @PostConstruct
      public void init() {
          System.out.println("init done");
      }
      
      public List<FlightFeasibilityException> getExceptions() {
          return exceptions;
      }
      
      public void setExceptions(List<FlightFeasibilityException> exceptions) {
          this.exceptions = exceptions;
      }
      
      public Variant getSelectedVariant() {
          return selectedVariant;
      }
      
      public void setSelectedVariant(Variant selectedVariant) {
          this.selectedVariant = selectedVariant;
      }
      
      public List<Variant> getVariants() {
          return variants;
      }
      
      public void setVariants(List<Variant> variants) {
          this.variants = variants;
      }
      
      public VariantConverter getVariantConverter() {
          return variantConverter;
      }
      
      public void setVariantConverter(VariantConverter variantConverter) {
          this.variantConverter = variantConverter;
      }
      

      }

    Converter

    @FacesConverter(forClass=Variant.class)
    public class VariantConverter implements Converter, Serializable {
    
        private static final long serialVersionUID = 7053414108213420057L;
    
        private VariantDao variantDao=new VariantDaoImpl();
    
        @Override
        public Object getAsObject(FacesContext context, UIComponent component,
                String value) {
            System.out.println("convert to Object " + value);
            Variant variant = variantDao.find(value);
            System.out.println("got variant " + variant.getCommercialName());
            return variant;
        }
    
        @Override
        public String getAsString(FacesContext context, UIComponent component,
                Object value) {
            System.out.println("convert to String " + value);
            return ((Variant) value).getCommercialName();
          }
    
        }
    

    and 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:h="http://java.sun.com/jsf/html"
        xmlns:a4j="http://richfaces.org/a4j"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Mission Page</title>
        <link href="./css/styles.css" rel="stylesheet" type="text/css" />
    </h:head>
    <h:body>
        <rich:panel>
            <f:facet name="header">
                Mission Information
            </f:facet>
            <a4j:outputPanel layout="block">
                <h:form>
                    <h:outputText style="font-weight: bold;" value="Mission Id: " />
                    <h:inputText id="missionId" label="missionId"
                        value="#{missionHandler.mission.id}" disabled="true"
                        style=" width : 50px;">
                    </h:inputText>
                    <h:outputText style="font-weight: bold;" value="Mission Status: " />
                    <h:inputText id="missionStatus" label="missionStatus"
                        value="#{missionHandler.mission.status}" disabled="true">
                    </h:inputText>
                </h:form>
                <br />
                <h:form>
                    <h:selectOneMenu value="#{missionHandler.selectedAircraftType}"
                        converter="#{missionHandler.acTypeConverter}">
                        <f:selectItem itemLabel="-- Select an A/C Type --" />
                        <f:selectItems value="#{missionHandler.aircraftTypes}" var="type"
                            itemValue="#{type}" itemLabel="#{type.typeOACI}" />
                        <f:ajax listener="#{missionHandler.changeSelectedAircraftType}"
                            render="@form" />
                    </h:selectOneMenu>
                    <h:selectOneMenu id="variantsMenu"
                        value="#{missionHandler.selectedVariant}"
                        converter="#{missionHandler.variantConverter}"
                        rendered="#{not empty missionHandler.selectedAircraftType}">
                        <f:selectItem itemLabel="-- Select a Variant --" />
                        <f:selectItems value="#{missionHandler.variants}" var="variant"
                            itemValue="#{variant}" itemLabel="#{variant.commercialName}" />
                    </h:selectOneMenu>
                </h:form>
            </a4j:outputPanel>
        </rich:panel>
    ….. A LOT OF OTHER THINGS …..
    </h:body>
    </html>
    
  • facewindu
    facewindu about 11 years
    I see what you mean: #EL expression can be used to access getters and setters (compared to old $EL expression that could access only getters) but this does not mean that the getter method will be called every time the value of the #EL expression changes ? You still need to call some sort of ajax or manual submit method.
  • BalusC
    BalusC about 11 years
    user1446127, JSF generates HTML. Rightclick page in browser and do view source. HTML is completely dumb and stateless unless you use JavaScript/ajax to make it somewhat interactive depending on user interface events (keyboard/mouse). This "issue" is completely unrelated to JSF. You'd have had exactly the same problem when manually writing HTML code, or when using another server side language which also generates HTML like PHP, ASP, etc.
  • facewindu
    facewindu about 11 years
    well, I jsut read some stuffs about OmniFaces. I will try and keep you posted. But from what I understand, I must load my list from the database at some point (let's says in the @PostConstruct method of my managed bean). What I don't understand with the OmniFaces item converters is : how are they able to convert String to Object without knowing about the list I loaded or the database ?
  • djmj
    djmj about 11 years
    1. why is it bad to do stuff in constructor? I only do stuff in @PostConstruct if there is a reason for (example viewParam dependant initialization logic), with 2. I aggree about the instantiating and passing the dao to the converter as a param is also an option which I mostly use. 3. I manage most of my entity converters myself, since the conversion depends very often on dynamic data.
  • kolossus
    kolossus about 11 years
    @djmj, it's primarily a point of good design as the @PostConstruct is a container-provided callback. Because it's container provided, it's a safer place to carry out initialization ops: If the method throws an unchecked exception the class MUST NOT be put into service except in the case of EJBs where the EJB can handle exceptions and even recover from them.--That is part of the contract for @PostConstruct. There are no guarantees for anything you do in a constructor. In future JEE versions, more safety features can be built into the annotation that your code will be prepared for.
  • kolossus
    kolossus about 11 years
    @djmj,wont managing your converters within your managed bean unnecessarily tie the converter to the scope of your managed bean? Probably resulting in a converter living longer than it should?
  • djmj
    djmj about 11 years
    Good point about the @PostConstruct and Exceptions, but I assumed that no Exceptions would be thrown in Constructor anyways.
  • djmj
    djmj about 11 years
    Converter This depends on the use case of my converters. If data is already loaded in bean as in the example there is no need to hit network or db again and the scope is already defined by the scope of the data objects. I wrote very generic converter for my entities which is within a client-dao instance which is created in the view-scoped beans. At the site I don't have to worry about converters since the component always uses the converter of the provided dao. Instead of writing many almost identical global converters This approach is very flexible if data is already in bean (dao) or lazy.
  • facewindu
    facewindu about 11 years
    I implemented OmniFaces items converter, and along with the other things listed in this thread, everything is good now. Thanks !
  • Mike Braun
    Mike Braun about 11 years
    Great it works! I think OmniFaces works by some reverse conversion algo: if 2 items convert to same string, it must be same object. Your list of objects from which you build select items must be there after post back. There'd also match by position in list. If you use this your list not only has to be there but it needs exact same objects in same order. This is same requirement as datatable has. View scope will satisfy this automatically.
  • Mike Braun
    Mike Braun about 11 years
    @djmj for viewParam an "@PostConstruct" is still too early. You need eg PreRenderView event handler for that.
  • djmj
    djmj about 11 years
    Oh yeah you are right thats how i did it. But I needed @PostConstruct for looking up other beans.