JSF SelectOneMenu с noSelectionOption с использованием метки в качестве значения? - PullRequest
6 голосов
/ 04 января 2011

На странице 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 равен SUPPOSED для отправки своей метки, а конвертер каким-то образом должен обрабатывать ее?

Ответы [ 2 ]

7 голосов
/ 05 января 2011

Моя проблема заключалась в переходе на использование атрибута конвертера 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 приводит к тому, что мой собственный конвертер всегда используется независимо от типа, и поэтому мне придется обрабатывать отправляемую метку как значение самостоятельно.

1 голос
/ 05 января 2011

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

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.

...