JSF tag to create a single-select menu.
A <h:selectOneMenu>
is a JSF UI component which generates a HTML <select>
element which allows you to bind the selected value to a managed bean property.
Basic example
You can use <f:selectItem>
to define menu options which get generated as HTML <option>
elements.
View:
<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>
Model:
private String selectedItem; // +getter +setter
public void submit() {
System.out.println("Selected item: " + selectedItem);
}
The itemValue
is what will be set as managed bean property. The itemLabel
is what will be displayed as option label in the UI. If the itemLabel
is omitted, then it will default to the same value as itemValue
.
You can preselect an item by giving the selectedItem
a value in the bean's (post)constructor. E.g.
@PostConstruct
public void init() {
selectedItem = "bar";
}
This will show the menu with "Bar label" preselected.
Dynamic list
You can use <f:selectItems>
to display a list which is dynamically populated in the backing bean. You can use javax.faces.model.SelectItem
to represent a pair of item value and label.
View:
<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>
Model:
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"));
}
The availableItems
can also be a SelectItem[]
. If you omit the item label and thus can use the item value as both option value and option label, then you can also use a List<String>
or String[]
instead.
private String selectedItem; // +getter +setter
private List<String> availableItems; // +getter (no setter necessary)
@PostConstruct
public void init() {
availableItems = Arrays.asList("foo", "bar", "baz");
}
A Set
is also supported.
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"));
}
Note that you should use a LinkedHashSet
to maintain the insertion order, or a TreeSet
if you want automatic sorting by value. A HashSet
is by nature unordered.
Dynamic list with groups
You can use SelectItemGroup
to group items.
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);
}
Here's how it look like in the browser (note that Group 1 and Group 2 are unselectable):
Dynamic map
The <f:selectItems>
also supports a Map<K, V>
where K
is to be represented as item label and V
is to be represented as item value.
View:
<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>
Model:
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");
}
If you want to swap the K
and V
in the view side so that you can populate the item values and labels the other way round like follows
@PostConstruct
public void init() {
availableItems = new LinkedHashMap<String, String>();
availableItems.put("foo", "Foo label");
availableItems.put("bar", "Bar label");
availableItems.put("baz", "Baz Label");
}
Then you first need to make sure that your environment supports EL 2.2 (part of Servlet 3.0, i.e. target server is Tomcat 7, Glassfish 3, etc and web.xml
is declared conform Servlet 3.0), then you can iterate over Map#entrySet()
in <f:selectItems>
as follows:
<f:selectItems value="#{bean.availableItems.entrySet()}" var="entry"
itemValue="#{entry.key}" itemLabel="#{entry.value}" />
Note that you should use a LinkedHashMap
to maintain the insertion order, or a TreeMap
if you want automatic sorting by map key. A HashMap
is by nature unordered.
Dynamic list of objects
Since JSF 2.0, you can also use a List<Bean>
or Bean[]
instead where Bean
is just an arbitrary javabean class. Assuming that you've a Country
bean with two String
properties code
and name
and you want to store the code
as selected item value and display the name
as item label, then you can use the var
attribute of <f:selectItems>
without the need to convert it to List<SelectItem>
or SelectItem[]
.
View:
<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>
Model:
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);
}
Storing objects as item value
From the example in the previous section, you can also set the whole Country
as a managed bean property. You'd only need to create a converter which converts between Country
and String
. A more concrete example can be found in those answers:
- F:selectItems showing the class not a value
- How to populate options of h:selectOneMenu from database?
Validation Error: Value is not valid
This dreaded error will occur when roughly the following happens under the JSF covers during submitting the form:
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");
}
You see, it will occur when the selected item does not equal any of the available items. This can in turn have 3 causes:
- The list of available items from bean has changed during the form submit.
- The
equals()
method of the object type behind the item is missing or broken. - If any used, the converter has returned the wrong item or even
null
ongetAsObject()
.
See also this answer: Validation Error: Value is not valid.
Static list of enums
You can also use an enum
as available items and selected item.
View:
<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>
Model:
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();
}
See also this answer how to change and internationalize item labels: How to use enum values in f:selectItem(s).
Populate a child menu
You can use the JSF 2.0 <f:ajax>
tag to dynamically populate a child menu depending on the selected item of the current menu. Here's an example which does that for countries-cities.
View:
<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>
Model in @ViewScoped
bean:
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);
}