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

На странице jsf "Создать нового пользователя" у меня есть SelectOneMenu с пользовательским конвертером и элемент selectOtem выбора 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>

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

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

2 ответа

Решение

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

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

@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. Моя теория состоит в том, что установка этого атрибута в true устанавливает тип значения в null с меткой в ​​качестве значения, заставляя его идти в специальный конвертер, всегда возвращающий null или что-то подобное Использование атрибута converter вместо forClass приводит к тому, что мой собственный конвертер всегда используется независимо от типа, и поэтому мне придется обрабатывать отправляемую метку как значение самостоятельно.

Одно из решений, которое работает, это просто добавить

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.

Другие вопросы по тегам