JSF SelectOneMenu with noSelectionOption using label as value?

On the jsf "create new user" page, I have SelectOneMenu with a custom converter and selectSelectionOption selectItem as follows: (irrelevant code is ommitted)

NewUser.xhtml

<h:form> <h:selectOneMenu value="#{newUserController.user.department}" required="true" converter="departmentConverter"> <f:selectItem itemLabel="Select a department" noSelectionOption="true"/> <f:selectItems value="#{newUserController.departments}" var="dep" itemLabel="#{dep.name}" itemValue="#{dep}"/> </h:selectOneMenu> <p:commandButton action="#{newUserController.saveUser}" value="#{bundle.Save}" ajax="false"/> </h:form> 

NewUserController.java

 @ManagedBean @ViewScoped public class NewUserController implements Serializable { private static final long serialVersionUID = 10L; @EJB private UserBean userBean; private List<Department> departments; private User user; public NewUserController () { } @PostConstruct public void init(){ user = new User(); departments = userBean.findAllDepartments(); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<Department> getDepartments(){ return departments; } public String saveUser() { // Business logic } } 

DepartmentConverter.java

 @FacesConverter(value="departmentConverter") public class DepartmentConverter extends EntityConverter { public DepartmentConverter(){ super(Department.class); } } 

Super converter for all objects

 public class EntityConverter<E> implements Converter{ protected Class<E> entityClass; public EntityConverter(Class<E> type) { entityClass = type; } @Override public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { if (value == null || value.length() == 0) { return null; } try { InitialContext ic = new InitialContext(); UserBean ub = (UserBean)ic.lookup("java:global/CompetenceRegister/UserBean"); return ub.find(entityClass, getKey(value)); } catch (NamingException e) { return null; } } Long getKey(String value) { Long key; key = Long.valueOf(value); return key; } String getStringKey(Long value) { StringBuilder sb = new StringBuilder(); sb.append(value); return sb.toString(); } @Override public String getAsString(FacesContext facesContext, UIComponent component, Object object) { if (object == null) { return null; } if (object instanceof AbstractEntity) { AbstractEntity e = (AbstractEntity) object; return getStringKey(e.getId()); } else throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + entityClass.getName()); } 

}

However, when I submit the form with the "Select Department" option selected, it sends a shortcut to getAsObject in the converter instead of a null value, which causes the converter to throw an exception in getKey (trying to convert a String containing id to Long). Setting the itemValue attribute of the selectItem element to null is not affected. Elements from the collection work great with the converter. Does anyone have an idea of ​​what causes this?

Update An interesting thing that I forgot to mention; if I remove the converter attribute from SelectOneMenu, noSelectionAttribute will work as it should, but since the converter does not know by default how to convert my objects, the post will fail when choosing the true department. Could this mean that noSelectionOption = true SUPPOSED sends its label instead, and the converter must somehow handle it?

+6
jsf jsf-2 converter selectonemenu
source share
2 answers

My problem was switching to using the SelectOneMenu converter attribute instead of using the forClass attribute for FacesConverter.

switching

 @FacesConverter(value="departmentConverter") public class DepartmentConverter extends EntityConverter { public DepartmentConverter(){ super(Department.class); } } 

to

 @FacesConverter(forClass=Department.class) public class DepartmentConverter extends EntityConverter { public DepartmentConverter(){ super(Department.class); } } 

causes my own converter to be used for real values, and the default converter (zero converter? I could not find the source code for it) is used when the NoSelectionOption attribute is set to true. My theory is that setting this attribute to true sets the type to null with the label as value, forcing it to go to a special converter that always returns zero or something like that. Using a converter attribute instead of forClass means that my own converter is always used regardless of type, and therefore I will have to process the label sent as the value myself.

+7
source share

One solution that works is to simply add

 try{ return ub.find(entityClass, getKey(value)); }catch(NumberFormatException e){ // Value isn't a long and thus not an id. return null; } 

in getAsObject in EntityConverter, but it looks like I'm fixing the problem in the wrong place. Sending a label as a value simply does not make sense, it really should send NULL.

+1
source share

All Articles