Dynamic Dependent List Boxes

Introduction

Dynamic dependent list boxes allow a “child” list box to refresh when a selection is made in a “parent” list box. The dynamic dependent list box returns to the server to requery the dependent list box’s items through an Ajax call.

The advantage to this asynchrone approach is immediate page response.

Using JSF 2.0

Both list boxes are component bound to the backing bean, using the “bind” attribute, instead of the “value” attribute. This enables programmatic access to the actual components. We use this to disable the child listbox when there is no parent value selected.

Another advantage for us is that validation of these UI bound components is skipped, so we do not yet have to know the list of values for the child list box at validation phase.

Backing Bean

@ManagedBean
public class DependentListboxController {

    private List<SelectItem> parentItems;
    private List<SelectItem> childItems;
    private HtmlSelectOneMenu uiSelectParent;
    private HtmlSelectOneMenu uiSelectChild;
    private String selectedParentChild;

    public String submitParentChild() {
        Object parentValue = uiSelectParent.getValue();
        Object childValue = !uiSelectChild.isDisabled() ? uiSelectChild.getValue() : null;
        selectedParentChild = parentValue + ":" + childValue;

        // Stay on current page.
        return null;
    }

    public void initializeMasterItems() {
        parentItems = new ArrayList<SelectItem>();
        parentItems.add(new SelectItem("key1", "Master value 1"));
        parentItems.add(new SelectItem("key2", "Master value 2"));
    }

    public void initializeChildItems() {
        Object masterValue = uiSelectParent.getValue();

        childItems = new ArrayList<SelectItem>();
        if ("key1".equals(masterValue)) {
            childItems.add(new SelectItem("key1-1", "Child value 1 (master1)"));
            childItems.add(new SelectItem("key1-2", "Child value 2 (master1)"));
        } else if ("key2".equals(masterValue)) {
            childItems.add(new SelectItem("key2-1", "Child value 1 (master2)"));
            childItems.add(new SelectItem("key2-2", "Child value 2 (master2)"));
        }

        // Disable child selectbox when no master value selected.
        boolean disabled = masterValue == null;
        uiSelectChild.setDisabled(disabled);
    }

    public List<SelectItem> getParentItems() {
        // Lazy initialize items.
        if (parentItems == null) {
            initializeMasterItems();
        }
        return parentItems;
    }

    public List<SelectItem> getChildItems() {
        // Lazy initialize items.
        if (childItems == null) {
            initializeChildItems();
        }
        return childItems;
    }

    // Getters and setters for UI components binding.

    public String getSelectedParentChild() {
        return selectedParentChild;
    }

    public HtmlSelectOneMenu getUiSelectParent() {
        return uiSelectParent;
    }

    public void setUiSelectParent(HtmlSelectOneMenu uiSelectParent) {
        this.uiSelectParent = uiSelectParent;
    }

    public HtmlSelectOneMenu getUiSelectChild() {
        return uiSelectChild;
    }

    public void setUiSelectChild(HtmlSelectOneMenu uiSelectChild) {
        this.uiSelectChild = uiSelectChild;
    }
}

Facelet

<h:form id="form">


<h2>Dynamic Dependent Selectboxes</h2>



<div>
    <h:selectOneMenu binding="#{dependentListboxController.uiSelectParent}">
        <f:selectItem itemLabel="-- Please Select Value --" noSelectionOption="true"/>
        <f:selectItems value="#{dependentListboxController.parentItems}"/>
        <!-- Submit on change. -->
        <f:ajax render="-form-selectChild"/>
    </h:selectOneMenu>

    <h:selectOneMenu binding="#{dependentListboxController.uiSelectChild}" id="selectChild">
        <f:selectItem itemLabel="-- Please Select Value --" noSelectionOption="true"/>
        <f:selectItems value="#{dependentListboxController.childItems}"/>
    </h:selectOneMenu>
</div>


<h:commandButton action="#{dependentListboxController.submitParentChild}" value="Submit Values">
    <f:ajax execute="@form" render="-form-selectedParentChild"/>
</h:commandButton>

<h:outputText value="Selected: #{dependentListboxController.selectedParentChild}" id="selectedParentChild"/>

</h:form>

Result

When no parent value selected, child listbox is disabled.


Parent value selected, child listbox is enabled.


Both parent and child value selected.

Demo

Sorry, no live demo.

 

Update 2011-09-08: As mentioned in this JSF 2 issue also, if you are using session scoped controllers it is perfectly fine to use just value bind select boxes. If it is an requirement to not use sessions, the programmatic approach in this example is a solution to that problem.

One thought on “Dynamic Dependent List Boxes”

Leave a Reply

Your email address will not be published. Required fields are marked *