JSF SelectOneMenu 如何使用标签作为值来添加 noSelectionOption?

6
在“创建新用户”的JSF页面中,我有一个SelectOneMenu,其中包含一个自定义转换器和一个noSelectionOption selectItem,如下所示:(省略了无关代码)
新用户.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);
    }
}

全能实体转换器

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

然而,当我提交表单并选择“选择一个部门”选项时,它会将标签发送到转换器的getAsObject方法中,而不是null,因此导致转换器在getKey中抛出异常(尝试将包含id的字符串转换为Long)。将selectItem的itemValue属性设置为null没有效果。除了转换器之外,集合中的项目完美运行。有人知道是什么原因造成了这种情况吗?

更新 我忘记提到的一个有趣的事情; 如果我从SelectOneMenu中删除转换器属性,则noSelectionAttribute将按照预期工作,但由于默认转换器不知道如何转换我的对象,因此在选择真正的部门时,提交将失败。这是否意味着noSelectionOption=true 应该发送其标签,并且某种程度上期望转换器处理它?


使用Jsf 2。抱歉没有澄清。 - Rasmus Franke
1
@Shervin:f:selectItem noSelectionOptionf:selectItems var 是JSF2的标志。 - BalusC
你的Facelet标记似乎是正确的。你是否包含了departmentConverter和newUserController的代码? - Brian Leathem
我没有包含转换器,因为它感觉不相关,问题在于它的调用方式而不是它自身的功能,控制器非常直接。但是我已经更新了帖子,所以现在你几乎可以看到所有内容了。 - Rasmus Franke
2个回答

7
我的问题是切换到使用SelectOneMenu的converter属性而不是使用FacesConverter的forClass属性。
切换
@FacesConverter(value="departmentConverter")
public class DepartmentConverter extends EntityConverter {
public DepartmentConverter(){
    super(Department.class);
}
}

为了

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

当NoSelectionOption属性设置为true时,会使用默认转换器(null转换器?我无法找到其源代码),导致我的自定义转换器用于实际值。我的理论是将此属性设置为true会将值的类型设置为null,并将标签作为值,从而使其始终进入特殊转换器,该转换器始终返回null或类似内容。使用converter属性而不是forClass属性,使得无论类型如何都始终使用我的自定义转换器,因此我必须自己处理发送的标签作为值的情况。


1
这个答案有些令人困惑:第一段表明了与后面所建议的完全相反的方向变化。 - Daniel C. Sobral

1
一个可行的解决方案是简单地添加


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

在EntityConverter的getAsObject方法中,但这感觉像是我在错误的地方修复问题。将标签作为值发送根本没有意义,它应该真正发送NULL。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接