Описание тега selectonemenu
А <h:selectOneMenu>
это компонент пользовательского интерфейса JSF, который генерирует HTML<select>
элемент, который позволяет привязать выбранное значение к свойству управляемого компонента.
Базовый пример
Вы можете использовать <f:selectItem>
для определения параметров меню, которые генерируются как HTML <option>
элементы.
Посмотреть:
<h:form>
<h:selectOneMenu value="#{bean.selectedItem}">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItem itemValue="foo" itemLabel="Foo label" />
<f:selectItem itemValue="bar" itemLabel="Bar label" />
<f:selectItem itemValue="baz" itemLabel="Baz label" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
Модель:
private String selectedItem; // +getter +setter
public void submit() {
System.out.println("Selected item: " + selectedItem);
}
В itemValue
то, что будет установлено как свойство управляемого компонента. ВitemLabel
то, что будет отображаться как метка параметра в пользовательском интерфейсе. ЕслиitemLabel
опущено, то по умолчанию будет установлено то же значение, что и itemValue
.
Вы можете предварительно выбрать элемент, указав selectedItem
значение в конструкторе bean-компонента (post). Например
@PostConstruct
public void init() {
selectedItem = "bar";
}
Появится меню с предварительно выбранным параметром "Метка панели".
Динамический список
Вы можете использовать <f:selectItems>
для отображения списка, который динамически заполняется в вспомогательном компоненте. Вы можете использовать javax.faces.model.SelectItem
для представления пары значения элемента и метки.
Посмотреть:
<h:form>
<h:selectOneMenu value="#{bean.selectedItem}">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.availableItems}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
Модель:
private String selectedItem; // +getter +setter
private List<SelectItem> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init() {
availableItems = new ArrayList<SelectItem>();
availableItems.add(new SelectItem("foo", "Foo label"));
availableItems.add(new SelectItem("bar", "Bar label"));
availableItems.add(new SelectItem("baz", "Baz label"));
}
В availableItems
также может быть SelectItem[]
. Если вы опускаете метку элемента и, таким образом, можете использовать значение элемента как значение параметра, так и метку параметра, вы также можете использоватьList<String>
или String[]
вместо.
private String selectedItem; // +getter +setter
private List<String> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init() {
availableItems = Arrays.asList("foo", "bar", "baz");
}
А Set
также поддерживается.
private String selectedItem; // +getter +setter
private Set<String> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init() {
availableItems = new LinkedHashSet<String>(Arrays.asList("foo", "bar", "baz"));
}
Обратите внимание, что вы должны использовать LinkedHashSet
для поддержания порядка размещения или TreeSet
если вам нужна автоматическая сортировка по значению. А HashSet
по своей природе неупорядоченный.
Динамический список с группами
Вы можете использовать SelectItemGroup
для группировки элементов.
private String selectedItem; // +getter +setter
private List<SelectItem> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init {
availableItems = new ArrayList<SelectItem>();
SelectItemGroup group1 = new SelectItemGroup("Group 1");
group1.setSelectItems(new SelectItem[] {
new SelectItem("Group 1 Value 1", "Group 1 Label 1"),
new SelectItem("Group 1 Value 2", "Group 1 Label 2"),
new SelectItem("Group 1 Value 3", "Group 1 Label 3")
});
availableItems.add(group1);
SelectItemGroup group2 = new SelectItemGroup("Group 2");
group2.setSelectItems(new SelectItem[] {
new SelectItem("Group 2 Value 1", "Group 2 Label 1"),
new SelectItem("Group 2 Value 2", "Group 2 Label 2"),
new SelectItem("Group 2 Value 3", "Group 2 Label 3")
});
availableItems.add(group2);
}
Вот как это выглядит в браузере (обратите внимание, что группу 1 и группу 2 выбрать нельзя):
Динамическая карта
В <f:selectItems>
также поддерживает Map<K, V>
где K
должен быть представлен как ярлык предмета и V
должен быть представлен как стоимость предмета.
Посмотреть:
<h:form>
<h:selectOneMenu value="#{bean.selectedItem}">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.availableItems}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
Модель:
private String selectedItem; // +getter +setter
private Map<String, String> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init() {
availableItems = new LinkedHashMap<String, String>();
availableItems.put("Foo label", "foo");
availableItems.put("Bar label", "bar");
availableItems.put("Baz label", "baz");
}
Если вы хотите поменять местами K
а также V
на стороне просмотра, чтобы вы могли заполнить значения элементов и пометить их наоборот, как показано ниже
@PostConstruct
public void init() {
availableItems = new LinkedHashMap<String, String>();
availableItems.put("foo", "Foo label");
availableItems.put("bar", "Bar label");
availableItems.put("baz", "Baz Label");
}
Затем вам сначала нужно убедиться, что ваша среда поддерживает EL 2.2 (часть Servlet 3.0, т.е. целевой сервер - Tomcat 7, Glassfish 3 и т. Д. web.xml
объявлен соответствующим сервлету 3.0), тогда вы можете перебрать Map#entrySet()
в <f:selectItems>
следующее:
<f:selectItems value="#{bean.availableItems.entrySet()}" var="entry"
itemValue="#{entry.key}" itemLabel="#{entry.value}" />
Обратите внимание, что вы должны использовать LinkedHashMap
для поддержания порядка размещения или TreeMap
если вам нужна автоматическая сортировка по ключу карты. А HashMap
по своей природе неупорядоченный.
Динамический список объектов
Начиная с JSF 2.0, вы также можете использовать List<Bean>
или Bean[]
вместо этого где Bean
это просто произвольный класс javabean. Предполагая, что у васCountry
фасоль с двумя String
свойства code
а также name
и вы хотите сохранить code
как значение выбранного элемента и отобразить name
как ярлык предмета, то вы можете использовать var
атрибут <f:selectItems>
без необходимости преобразовывать его в List<SelectItem>
или SelectItem[]
.
Посмотреть:
<h:form>
<h:selectOneMenu value="#{bean.countryCode}">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.countries}" var="country"
itemValue="#{country.code}" itemLabel="#{country.name}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
Модель:
private String countryCode; // +getter +setter
private List<Country> countries; // +getter (no setter necessary)
@EJB
private DataService service;
@PostConstruct
public void init() {
countries = service.getCountries();
// If this is a static list, you'd rather put this
// in a separate application scoped bean instead.
}
public void submit() {
System.out.println("Selected country code: " + countryCode);
}
Сохранение объектов как значения элемента
Из примера в предыдущем разделе вы также можете установить все Country
как управляемое свойство bean-компонента. Вам нужно только создать конвертер, который конвертирует междуCountry
а также String
. В этих ответах можно найти более конкретный пример:
- F:selectItems показывает класс, а не значение
- Как заполнить параметры h:selectOneMenu из базы данных?
Ошибка проверки: недопустимое значение
Эта ужасная ошибка возникает, когда во время отправки формы под JSF происходит примерно следующее:
String submittedValue = request.getParameter(component.getClientId());
Converter converter = component.getConverter();
Object selectedItem = (converter != null) ? converter.getAsObject(context, component, submittedValue) : submittedValue;
boolean valid = false;
for (Object availableItem : bean.getAvailableItems()) {
if (selectedItem.equals(availableItem)) {
valid = true;
break;
}
}
if (!valid) {
throw new ValidatorException("Validation Error: Value is not valid");
}
Видите ли, это произойдет, когда выбранный элемент не равен ни одному из доступных элементов. Это, в свою очередь, может иметь 3 причины:
- Список доступных элементов bean-компонента изменился во время отправки формы.
- В
equals()
метод типа объекта позади элемента отсутствует или неисправен. - Если они использовались, конвертер вернул не тот элемент или даже
null
наgetAsObject()
.
См. Также ответ: Ошибка проверки: недопустимое значение.
Статический список перечислений
Вы также можете использовать enum
как доступные элементы и выбранный элемент.
Посмотреть:
<h:form>
<h:selectOneMenu value="#{bean.selectedItem}">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.availableItems}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{bean.submit}" />
</h:form>
Модель:
public enum Option {
FOO, BAR, BAZ;
}
private Option selectedItem; // +getter +setter
public void submit() {
System.out.println("Selected item: " + selectedItem);
}
public Option[] getAvailableItems() {
return Option.values();
}
См. Также этот ответ, как изменять и интернационализировать метки элементов: Как использовать значения перечисления в f:selectItem(s).
Заполнить дочернее меню
Вы можете использовать JSF 2.0 <f:ajax>
тег для динамического заполнения дочернего меню в зависимости от выбранного элемента текущего меню. Вот пример, который делает это для стран-городов.
Посмотреть:
<h:selectOneMenu value="#{bean.country}" converter="countryConverter">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.countries}" var="country"
itemValue="#{country}" itemLabel="#{country.name}" />
<f:ajax listener="#{bean.loadCities}" render="cities" />
</h:selectOneMenu>
<h:selectOneMenu id="cities" value="#{bean.city}" converter="cityConverter">
<f:selectItem itemValue="#{null}" itemLabel="-- select one --" />
<f:selectItems value="#{bean.cities}" var="city"
itemValue="#{city}" itemLabel="#{city.name}" />
</h:selectOneMenu>
Модель в @ViewScoped
фасоль:
private Country country; // +getter +setter
private City city; // +getter +setter
private List<Country> countries; // +getter
private List<City> cities; // +getter
@EJB
private DataService service;
@PostConstruct
public void loadCountries() {
countries = service.getCountries();
}
public void loadCities() {
cities = service.getCities(country);
}