You can use <f:ajax>
for this. When nested in an input component such as <h:selectOneMenu>
, then it will by default be invoked when the input value is changed. You can specify a listener
method which could prepopulate the data for the next component based on the current input value, and you can specify the client ID of that next component in render
in order to show the prepopulated data.
<h:selectOneMenu value="#{bean.country}">
<f:selectItems value="#{bean.countries}" />
<f:ajax listener="#{bean.changeCountry}" render="cities" />
</h:selectOneMenu>
<h:panelGroup id="cities">
<h:selectOneMenu value="#{bean.city}" rendered="#{not empty bean.cities}">
<f:selectItems value="#{bean.cities}" />
</h:selectOneMenu>
</h:panelGroup>
The bean must be in at least the view scope (not request):
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
private String country; // +getter+setter
private String city; // +getter+setter
private List<String> countries; // +getter
private List<String> cities; // +getter
@EJB
private LocationService locationService;
@PostConstruct
public void init() {
countries = locationService.getCountries();
}
public void changeCountry() {
cities = locationService.getCities(country);
}
// ...
}
You can of course also use a Map<String, String>
instead of a List<String>
. The map key becomes the option label and the map value becomes the option value. You only need to keep in mind that a HashMap
is by nature unordered. You'd prefer using LinkedHashMap
instead to display the items in Map
insertion order.
See also:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…