selectOneMenu selected value
Solution 1
A couple of notes on your code:
Doing stuff in the constructor of the managed bean==bad design, use
@PostConstructor
instead.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.
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.
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.
facewindu
Updated on June 25, 2022Comments
-
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 themissionHandler
managedBean to be updated automatically. But this is not the case. ThevariantConverter
converter is not even called to convert the String "variant.commercialName" to a realVariant
object. The variant object implementshashCode()
andequals()
- 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 about 11 yearsI 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 about 11 yearsuser1446127, 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 about 11 yearswell, 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 about 11 years1. 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 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 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 about 11 yearsGood point about the
@PostConstruct
and Exceptions, but I assumed that no Exceptions would be thrown in Constructor anyways. -
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 about 11 yearsI implemented OmniFaces items converter, and along with the other things listed in this thread, everything is good now. Thanks !
-
Mike Braun about 11 yearsGreat 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 about 11 years@djmj for viewParam an "@PostConstruct" is still too early. You need eg PreRenderView event handler for that.
-
djmj about 11 yearsOh yeah you are right thats how i did it. But I needed @PostConstruct for looking up other beans.