Ответ 1
Введение
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
является то, что он возвращает представление unique 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 + "]";
}
См. также:
- Как заполнить параметры h: selectOneMenu из базы данных?
- Общий конвертер сущностей JSF - так что вам не нужно писать конвертер для каждого объекта.
- Использование перечислений в selectsems JSF - перечисления должны обрабатываться несколько иначе
- Как добавить @EJB, @PersistenceContext, @Inject, @Autowired и т.д. в @FacesConverter?
Несвязанный к проблеме, поскольку 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;