Введение
JSF генерирует HTML. HTML в терминах Java в основном один большой String
. Чтобы представить объекты Java в HTML, они должны быть преобразованы в String
. Кроме того, при отправке формы HTML отправленные значения обрабатываются как String
в параметрах HTTP-запроса. Под обложками JSF извлекает их из HttpServletRequest#getParameter()
, который возвращает String
.
Для преобразования между нестандартными объектами Java (т. Е. Не String
, Number
или Boolean
, для которых EL имеет встроенные преобразования, или Date
, для которых JSF предоставляет встроенные <f:convertDateTime>
), вы действительно должны предоставить пользовательский Converter
. SelectItem
не имеет никакого специального назначения вообще. Это просто пережиток JSF 1.x, когда было невозможно поставить, например, List<Warehouse>
прямо на <f:selectItems>
. Он также не имеет особого отношения к этикеткам и конверсии.
getAsString ()
Вам необходимо реализовать метод getAsString()
таким образом, чтобы требуемый Java-объект был представлен в уникальном String
представлении, которое можно использовать в качестве параметра HTTP-запроса. Обычно здесь используется технический идентификатор (первичный ключ базы данных).
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof Warehouse) {
return String.valueOf(((Warehouse) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(modelValue + " is not a valid Warehouse"));
}
}
Обратите внимание, что возвращение пустой строки в случае нулевого / пустого значения модели является значительным и требуется для javadoc . См. Также Использование «Пожалуйста, выберите» f: selectItem с нулевым / пустым значением внутри p: selectOneMenu .
getAsObject ()
Вам необходимо реализовать getAsObject()
таким образом, чтобы точно , чтобы String
представление, возвращаемое getAsString()
, могло быть преобразовано обратно в точно тот же Java-объект, который указан как modelValue
в getAsString()
.
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return warehouseService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Warehouse ID"), e);
}
}
Другими словами, у вас должна быть техническая возможность вернуть возвращенный объект как modelValue
аргумент getAsString()
, а затем вернуть полученную строку как submittedValue
аргумент getAsObject()
в бесконечном цикле.
Использование
Наконец, просто аннотируйте Converter
с помощью @FacesConverter
, чтобы подключить рассматриваемый тип объекта, затем JSF автоматически позаботится о преобразовании, когда тип Warehouse
когда-либо появится на рисунке:
@FacesConverter(forClass=Warehouse.class)
Это был "канонический" подход JSF. В конце концов, он не очень эффективен, поскольку мог бы просто взять предмет из <f:selectItems>
. Но наиболее важным моментом Converter
является то, что он возвращает уникальное String
представление, так что объект Java может быть идентифицирован простым String
, подходящим для передачи в HTTP и HTML.
Общий конвертер на основе toString ()
Утилита JSF OmniFaces имеет SelectItemsConverter
, которая работает на основе toString()
результатов объекта. Таким образом, вам больше не нужно возиться с getAsObject()
и дорогими бизнес-операциями / операциями с базами данных. Некоторые конкретные примеры использования см. Также showcase .
Чтобы использовать его, просто зарегистрируйте его как показано ниже:
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
И убедитесь, что toString()
вашей Warehouse
сущности возвращает уникальное представление сущности. Например, вы можете напрямую вернуть идентификатор:
@Override
public String toString() {
return String.valueOf(id);
}
Или что-то более читаемое / многоразовое использование:
@Override
public String toString() {
return "Warehouse[id=" + id + "]";
}
Смотри также:
Не имеет отношения к проблеме , поскольку в JSF 2.0 больше не требуется явно иметь значение List<SelectItem>
как <f:selectItem>
. Достаточно было бы и List<Warehouse>
.
<h:selectOneMenu value="#{bean.selectedWarehouse}">
<f:selectItem itemLabel="Choose one .." itemValue="#{null}" />
<f:selectItems value="#{bean.availableWarehouses}" var="warehouse"
itemLabel="#{warehouse.name}" itemValue="#{warehouse}" />
</h:selectOneMenu>
private Warehouse selectedWarehouse;
private List<Warehouse> availableWarehouses;