I am trying to make 4 dependent / cascading selection components. In this question, the selection component happens to be a <h:selectOneMenu>
, but this is of course applicable on any other kind of selection component extending from UISelectOne
/UISelectMany
superclass, such as <h:selectManyCheckbox>
or PrimeFaces <p:selectCheckboxMenu>
, <p:selectManyMenu>
, etc.
When the user chooses an item from the first menu, the second menu will show dependent data and when the user chooses item from the second one , the third one will show dependent data and so on.
The user will see items on the first menu only and the other ones will be blank. If he chooses an item on the first menu the second one will show data but the third and the fourth will remain blank, and so on. The user must eventually choose entries from all the 4 menus.
<h:selectOneMenu id="first" value="#{nodes.selectState}">
<f:selectItems value="#{nodes.stateList}"/>
<f:ajax render="second">
</h:selectOneMenu>
<h:selectOneMenu id="second" value="#{nodes.selectCity}">
<f:selectItems value="#{nodes.cityList}"/>
<f:ajax render="third">
</h:selectOneMenu>
<h:selectOneMenu id="third" value="#{nodes.selectRegion}">
<f:selectItems value="#{nodes.regionList}"/>
<f:ajax render="fourth">
</h:selectOneMenu>
<h:selectOneMenu id="fourth" value="#{nodes.selectStation}">
<f:selectItems value="#{nodes.stationList}"/>
</h:selectOneMenu>
Nodes bean
private String selectState; //+setters, getters
private String selectCity; //+setters, getters
private String selectRegion; //+setters, getters
private String selectStation; //+setters, getters
private List<SelectItem> stateList; //+setters, getters
private List<SelectItem> cityList; //+setters, getters
private List<SelectItem> regionList; //+setters, getters
private List<SelectItem> stationList; //+setters, getters
public getStateList(){
stateList= new ArrayList<SelectItem>();
stateList.add(new SelectItem("A"));
}
public getCityList(){
CityList= new ArrayList<SelectItem>();
if(selectState.equals("A")){
CityList.add(new SelectItem("B"));
}
}
public getRegionList(){
RegionList= new ArrayList<SelectItem>();
if(selectCity.equals("B")){
RegionList.add(new SelectItem("C"));
}
}
public getStationList(){
StationList= new ArrayList<SelectItem>();
if(selectRegion.equals("C")){
StationList.add(new SelectItem("D"));
}
}
It's only working for the first 2 menus. The other 2 menus get null
values.
Put the bean in the view scope and get rid of any business logic in getter methods.
The bean must be placed in the view scope so that all previous selections and new available items are remembered for subsequent postbacks, otherwise things will fail when JSF needs to validate the selected item against the list of available items which was prepopulated in a previous selection, or when e.g. rendered
attribute depends on a condition which was only set in a previous request.
The getter methods may not contain any business logic as they will also be invoked during among others the validations phase. You should use <f:ajax listener>
/<p:ajax listener>
to perform business logic based on a change. You should in the listener method also explicitly clear out selected values of child selection components. You can use <f:ajax render>
/<p:ajax update>
to update the contents of child selection components.
Thus, so:
<h:selectOneMenu id="state" value="#{nodes.selectedState}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableStates}" />
<f:ajax listener="#{nodes.changeState}" render="city region station" />
</h:selectOneMenu>
<h:selectOneMenu id="city" value="#{nodes.selectedCity}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableCities}" />
<f:ajax listener="#{nodes.changeCity}" render="region station" />
</h:selectOneMenu>
<h:selectOneMenu id="region" value="#{nodes.selectedRegion}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableRegions}" />
<f:ajax listener="#{nodes.changeRegion}" render="station" />
</h:selectOneMenu>
<h:selectOneMenu id="station" value="#{nodes.selectedStation}">
<f:selectItem itemValue="#{null}" itemLabel="-- select --" />
<f:selectItems value="#{nodes.availableStations}" />
</h:selectOneMenu>
with
@Named
@ViewScoped
public class Nodes {
private String selectedState; // getter+setter
private String selectedCity; // getter+setter
private String selectedRegion; // getter+setter
private String selectedStation; // getter+setter
private List<SelectItem> availableStates; // getter (no setter necessary!)
private List<SelectItem> availableCities; // getter (no setter necessary!)
private List<SelectItem> availableRegions; // getter (no setter necessary!)
private List<SelectItem> availableStations; // getter (no setter necessary!)
@EJB
private SomeService someService;
@PostConstruct
public void init() {
availableStates = someService.listStates();
}
public void changeState(AjaxBehaviorEvent event) {
availableCities = someService.listCities(selectedState);
selectedCity = selectedRegion = selectedStation = null;
availableRegions = availableStations = null;
}
public void changeCity(AjaxBehaviorEvent event) {
availableRegions = someService.listRegions(selectedCity);
selectedRegion = selectedStation = null;
availableStations = null;
}
public void changeRegion(AjaxBehaviorEvent event) {
availableStations = someService.listStations(selectedRegion);
selectedStation = null;
}
// Generate necessary getters+setters here. You should not change them.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With