Общий преобразователь h: selectOneMenu для всех сущностей без повторного вызова БД

Я хочу получить выбранный объект из <h:selectOneMenu>, но проблема в том, что я не смог найти универсальный конвертер для всех типов сущностей.

Мой первый вопрос: существует ли универсальный конвертер для всех типов сущностей? Я не хочу снова писать еще один преобразователь для каждого другого объекта. Мой второй вопрос: есть ли способ получить выбранный объект без конвертера? Я не хочу снова и снова вызывать БД.

У меня есть сущность Car со свойствами id и name.


person Turgut Dsfadfa    schedule 22.04.2013    source источник


Ответы (3)


Мой первый вопрос: существует ли универсальный конвертер для всех типов сущностей?

Это действительно не существует в стандартном JSF. Утилитная библиотека JSF OmniFaces имеет в своем ассортименте такой преобразователь, omnifaces.SelectItemsConverter. Все, что вам нужно сделать, это объявить его как преобразователь компонента UISelectOne или UISelectMany следующим образом:

<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">

См. также презентацию SelectItemsConverter. Этот преобразователь опирается на toString() элемента объекта. Есть еще один, omnifaces.SelectItemsIndexConverter, который вместо этого опирается на индекс элемента объекта в списке параметров, см. также витрина SelectItemsIndexConverter.

В настоящее время нет других библиотек компонентов / утилит JSF, предлагающих то же самое.


Второй вопрос: есть ли способ получить выбранный объект без конвертера?

Нет. Просто используйте OmniFaces, чтобы вам не нужно было создавать собственный конвертер, который попадает в БД. Или, если вы хотите выйти за рамки, создайте собственный рендерер для <h:selectOneMenu>, который отображает индекс элемента как значение параметра и может установить его как значение модели, но это намного больше работы, чем простой конвертер, и вам все равно нужно делать дополнительную работу, чтобы получить нужный объект из списка на основе индекса, что просто не имеет никакого смысла.

Смотрите также:

person BalusC    schedule 22.04.2013
comment
Могу ли я получить индекс выбранного значения из selectonemenu и получить выбранное значение из списка, который я привязал к selectonemenu? - person Turgut Dsfadfa; 22.04.2013
comment
Почему эта информация актуальна для поддерживающего компонента? В любом случае... просто сделайте availableItems.indexOf(selectedItem). - person BalusC; 22.04.2013
comment
Проблема в том, как я могу получить индекс selectedItem или selectedItem? что вы подразумеваете под availableItems.indexOf(selectedItem) !? - person Turgut Dsfadfa; 22.04.2013
comment
Извините, я надеялся, что имена переменных достаточно самодокументированы, чтобы человек с достаточными способностями к логическому мышлению мог легко понять, что они представляют. availableItems — это просто значение, которое вы привязали к <f:selectItems>, а selectedItem — это просто значение, которое вы привязали к <h:selectOneMenu>. Эта строка может быть включена в метод действия (слушателя). Опять же, я не уверен, как имеет смысл вычислять индекс, если у вас уже есть выбранный элемент. - person BalusC; 22.04.2013
comment
вот мой конкретный вопрос stackoverflow.com/questions/16150154/ - person Turgut Dsfadfa; 22.04.2013
comment
Вы не сможете получить выбранный товар без конвертера. Как ответили, используйте omnifaces.SelectItemsConverter, чтобы вам не нужно было создавать собственный конвертер. - person BalusC; 22.04.2013
comment
@BalusC, вероятно, OP хочет привязать значение не к классам моделей как Car, а к простым строкам, например, <h:selectOneMenu value="#{bean.carName}"> с <f:selectItems value="#{bean.availableCarNames}">? - person skuntsel; 22.04.2013
comment
@skuntsel: этого легко добиться с помощью <f:selectItems var>. См. также нашу вики-страницу selectonemenu: stackoverflow.com/tags/selectonemenu/info - person BalusC; 22.04.2013
comment
Я это знаю :) Просто размышляя над этим вопросом и его дубликатом, принимая во внимание его нежелание использовать конвертеры, я подумал, что единственное, что осталось, это сохранение выбранного значения в виде простой строки . - person skuntsel; 22.04.2013

Похоже, что должен быть общий преобразователь, чтобы вы могли легко выбрать объект из раскрывающегося списка без необходимости писать преобразователь для каждого типа объекта и без необходимости вызывать базу данных (как показано в большинстве примеров). Но я не знаю, поэтому я написал свой собственный конвертер, чтобы сделать это. Обратите внимание, что преобразователь ожидает, что объект будет иметь метод getId(), который возвращает какой-то уникальный идентификатор. Если это не так, он потерпит неудачу. Вы можете добавить логику к getMethodName(), если вам нужно программно определить имя метода, который будет использоваться в качестве ключа. Обратите внимание, что в нашем проекте мы используем Seam. Если вы не используете Seam, части NO_SELECTION_VALUE, вероятно, можно удалить, а также три аннотации в классе.

Этот код был вдохновлен: http://arjan-tijms.omnifaces.org/2011/12/automatic-to-object-conversion-in-jsf.html

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;

import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import javax.faces.model.SelectItem;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.faces.Converter;
import org.jboss.seam.annotations.intercept.BypassInterceptors;

/**
 * @author: Jason Wheeler
 * @description Converter for lists (SelectOneMenu, SelectManyMenu, etc)
 * @created: 09/05/2013
 */

@Name("listConverter")
@BypassInterceptors
@Converter
public class ListConverter implements javax.faces.convert.Converter {
    private String NO_SELECTION_VALUE = "org.jboss.seam.ui.NoSelectionConverter.noSelectionValue";

    @Override
    public String getAsString(FacesContext facesContext, UIComponent component, Object obj) {
        if (obj == null) {
            return NO_SELECTION_VALUE;
        } else {
            try {
                Method method = obj.getClass().getMethod(getMethodName(obj));
                return String.valueOf(method.invoke(obj));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public String getMethodName(Object obj) {
        return "getId";
    }

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String val) throws ConverterException {
        if (val == null) {
            return null;
        } else if (val.equals(NO_SELECTION_VALUE)) {
            return null;
        } else {
            for (SelectItem item : getSelectItems(component)) {
                if (val.equals(getAsString(facesContext, component, item.getValue()))) {
                    return item.getValue();
                }
            }
            return null;
        }
    }

    protected Collection<SelectItem> getSelectItems(UIComponent component) {
        Collection<SelectItem> collection = new ArrayList<SelectItem>();

        for (UIComponent child : component.getChildren()) {
            if (child instanceof UISelectItem) {
                UISelectItem ui = (UISelectItem) child;
                SelectItem item = (SelectItem) ui.getValue();
                collection.add(item);
            } else if (child instanceof UISelectItems) {
                UISelectItems ui = (UISelectItems) child;
                Object value = ui.getValue();
                collection.addAll((Collection<SelectItem>) value);
            }
        }

        return collection;
    }
}
person Jason Wheeler    schedule 05.09.2013

Я только что взял код @Bigwheels, внес некоторые изменения для работы с JSF 2.0, и это решило мою проблему:

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import javax.faces.model.SelectItem;
import javax.persistence.Id;

@FacesConverter("selectItemConverter")
public class SelectItemConverter implements javax.faces.convert.Converter {

    private String NO_SELECTION_VALUE = "SELECIONE";

    @Override
    public String getAsString(FacesContext facesContext, UIComponent component, Object obj) {
        if (obj == null) {
            return NO_SELECTION_VALUE;
        } else {
            try {
                Method method = obj.getClass().getMethod(getIdMethodName(obj));
                return String.valueOf(method.invoke(obj));
            } catch (Exception e) {
                throw new ConverterException(e);
            }
        }
    }

    public String getIdMethodName(Object obj) {
        try {
            Field[] fieldList = obj.getClass().getDeclaredFields();

            Field id = null;
            for (Field field : fieldList) {
                if(field.isAnnotationPresent(Id.class)) {
                    id = field;
                    break;
                }
            }
            return "get" + capitalize(id.getName());
        } catch(Exception ex) {
            throw new ConverterException(ex);
        }
    }

    private String capitalize(final String line) {
        return Character.toUpperCase(line.charAt(0)) + line.substring(1);
    }

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String val) throws ConverterException {
        if (val == null) {
            return null;
        } else if (val.equals(NO_SELECTION_VALUE)) {
            return null;
        } else {
            for (Object item : getSelectItems(component)) {
                if (val.equals(getAsString(facesContext, component, item))) {
                    return item;
                }
            }
            return null;
        }
    }

    protected List getSelectItems(UIComponent component) {
        List list = new ArrayList();
        for (UIComponent child : component.getChildren()) {
            if (child instanceof UISelectItem) {
                UISelectItem ui = (UISelectItem) child;
                SelectItem item = (SelectItem) ui.getValue();
                list.add(item);
            } else if (child instanceof UISelectItems) {
                UISelectItems ui = (UISelectItems) child;
                Object value = ui.getValue();
                list.addAll((Collection<SelectItem>) value);
            }
        }
        return list;
    }
}
person Jaumzera    schedule 06.06.2016