JSF SelectOneMenu с noSelectionOption с использованием метки в качестве значения?

На jsf-странице «создать нового пользователя» у меня есть SelectOneMenu с пользовательским конвертером и selectItem noSelectionOption следующим образом: (нерелевантный код опущен)

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>

Ньюусерконтроллер.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 в преобразователе вместо нуля, что приводит к тому, что преобразователь выдает исключение в getKey (пытается преобразовать строку, содержащую идентификатор, в тип Long). Установка для атрибута itemValue объекта selectItem значения null не имеет никакого эффекта. В противном случае элементы из коллекции прекрасно работают с конвертером. Кто-нибудь знает, что вызывает это?

Обновление Интересная вещь, которую я забыл упомянуть; если я удалю атрибут конвертера из SelectOneMenu, атрибут noSelectionAttribute будет работать как надо, но, поскольку преобразователь по умолчанию не знает, как преобразовать мои объекты, сообщение не будет выполнено при выборе истинного отдела. Может ли это означать, что параметру noSelectionOption=true ПРЕДПОЛАГАЕТСЯ вместо этого отправлять свою метку, и ожидается, что преобразователь каким-то образом обработает ее?


person Rasmus Franke    schedule 04.01.2011    source источник
comment
Использование Jsf 2. Извините, что не пояснил.   -  person Rasmus Franke    schedule 04.01.2011
comment
@Shervin: f:selectItem noSelectionOption и f:selectItems var указывают на JSF2.   -  person BalusC    schedule 04.01.2011
comment
Ваша разметка лицевой панели кажется правильной. Как насчет включения кода для вашего DepartmentConverter и для вашего newUserController.   -  person Brian Leathem    schedule 05.01.2011
comment
Я не включал преобразователь, так как он казался неуместным, потому что проблема заключалась в том, как он называется, а не в его собственной функциональности, а контроллер очень прост. Однако я обновил пост, так что теперь вы можете видеть почти все.   -  person Rasmus Franke    schedule 05.01.2011


Ответы (2)


Моя проблема заключалась в переключении на использование атрибута конвертера SelectOneMenu вместо использования атрибута forClass FacesConverter.

переключение

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

приводит к тому, что мой собственный преобразователь используется для реальных значений, в то время как преобразователь по умолчанию (нулевой преобразователь? Мне не удалось найти его исходный код) используется, когда для атрибута NoSelectionOption установлено значение true. Моя теория заключается в том, что установка этого атрибута в значение true устанавливает тип значения равным null с меткой в ​​качестве значения, заставляя его переходить к специальному преобразователю, всегда возвращающему null или что-то подобное. Использование атрибута конвертера вместо forClass приводит к тому, что мой собственный преобразователь всегда используется независимо от типа, и поэтому мне пришлось бы самому обрабатывать метку, отправляемую как значение.

person Rasmus Franke    schedule 05.01.2011
comment
Этот ответ сбивает с толку: первый абзац указывает на изменение в направлении, прямо противоположном предложенному последним. - person Daniel C. Sobral; 21.12.2012

Одно из работающих решений — просто добавить

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

в getAsObject в EntityConverter, но мне кажется, что я исправляю проблему не в том месте. Отправка метки как значения просто не имеет смысла, она действительно должна отправлять NULL.

person Rasmus Franke    schedule 05.01.2011