2011-01-04 19 views
6

En una página "Crear nuevo usuario" JSF, tengo una SelectOneMenu con un convertidor de medida y un selectItem noSelectionOption como esto: (código irrelevante omite,)JSF SelectOneMenu with noSelectionOption usando la etiqueta como valor?

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); 
    } 
} 

Súper convertidor para todas las entidades

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()); 
} 

}

Sin embargo, cuando puedo enviar el formulario con la opción "Seleccionar un departamento" elegido, envía la etiquetaa getAsObject en el convertidor en lugar de null , lo que provoca que el convertidor genere una excepción en getKey (intenta convertir una cadena que contiene una identificación en una larga). Establecer el atributo itemValue del selectItem en nulo no tiene ningún efecto. Los elementos de la colección funcionan a la perfección con el convertidor. ¿Alguien tiene alguna idea de lo que está causando esto?

Actualización Una cosa interesante que olvidé mencionar; si elimino el atributo del convertidor de SelectOneMenu, noSelectionAttribute funciona como debería, pero dado que el convertidor predeterminado no sabe cómo convertir mis objetos, la publicación falla al seleccionar un departamento verdadero en su lugar. ¿Esto puede significar que noSelectionOption = true es SUPPOSED para enviar su etiqueta en su lugar y se espera que el convertidor lo maneje de alguna manera?

+0

¿Esto es jsf1 o jsf2? –

+0

Usando Jsf 2. Lo siento por no aclarar. –

+1

@Shervin: 'f: selectItem noSelectionOption' y' f: selectItems var' indica JSF2. – BalusC

Respuesta

7

Mi problema fue cambiar al uso del atributo convertidor de SelectOneMenu en lugar de utilizar el atributo forClass de FacesConverter.

conmutación

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

a

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

hace que mi propio convertidor para ser utilizado para valores reales mientras que el convertidor predeterminado (convertidor nula? No he sido capaz de encontrar el código fuente para ello) se usa cuando el atributo NoSelectionOption se establece en verdadero. Mi teoría es que establecer este atributo en true establece el tipo de valor a null con la etiqueta como valor que hace que vaya a un convertidor especial que siempre devuelve nulo o algo similar. Usar el atributo convertidor en lugar de forClass hace que mi propio convertidor siempre se use independientemente del tipo, y así tendría que manejar la etiqueta que se envía como valor yo mismo.

+1

Esta respuesta es confusa: el primer párrafo indica un cambio en la dirección exactamente opuesta a la sugerida. –

1

Una solución que funciona es simplemente agregar

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

a la getAsObject en EntityConverter, pero esto se siente como que estoy arreglando el problema en el lugar equivocado. Enviar la etiqueta como valor simplemente no tiene sentido, realmente debería enviar NULL.

Cuestiones relacionadas