Описание тега selectonemenu

Тег JSF для создания меню с одним выбором.

А <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. В этих ответах можно найти более конкретный пример:

Ошибка проверки: недопустимое значение

Эта ужасная ошибка возникает, когда во время отправки формы под 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 причины:

  1. Список доступных элементов bean-компонента изменился во время отправки формы.
  2. В equals() метод типа объекта позади элемента отсутствует или неисправен.
  3. Если они использовались, конвертер вернул не тот элемент или даже 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);
}