Thursday, July 05, 2012

PrimeFaces AJAX Enabled <p:selectOneMenu />

I saw a question posed on stackoverflow called Trouble with Primefaces 3.0.M2 SelectOneMenu Ajax behavior and I had just done an example a couple of months ago on a car trip so I decided to post my solution here, and reference it.

Essentially the problem is how to control the list of items in a second <p:selectOneMenu/< from the selection made in the first menu. The question comes up frequently enough that I decided to write up a demo application.

The application was developed using NetBeans 7.1.2 and PrimeFaces 3.2 originally. It has been updated to use PrimeFaces 3.3.1 which is the most current at the time of writing. I validated it on GlassFish 3.1.2.

 The Apache Maven project can be downloaded from here: primefaces-ajax-selectone.zip

This demo wants the user to pick a state, and then a city. A key problem is not just the update, but not displaying city values if a state is not selected. Also erasing the selected city, if the state has changed.

index.xhtml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      >
    <h:head>
        <title>PrimeFaces AJAX Enabled SelectOneMenu</title>
    </h:head>
    <h:body>
        <h:form>
            <p:panel header="Address">
                <h:panelGrid columns="4">
                    <h:outputLabel value="State:" for="states"/>
                    <p:selectOneMenu id="states" value="#{dataBean.selectedState}"
                                     valueChangeListener="#{dataBean.stateChangeListener}" style="width: 150px;">
                        <f:selectItem itemLabel="" itemValue=""/>
                        <f:selectItems value="#{dataBean.states}"/>
                        <p:ajax event="change" update="cities, cs"/>
                    </p:selectOneMenu>
                    <h:outputLabel value="City:" for="cities"/>
                    <p:selectOneMenu id="cities" value="#{dataBean.selectedCity}" style="width: 150px;">
                        <f:selectItem itemLabel="" itemValue=""/>
                        <f:selectItems value="#{dataBean.cities}"/>
                        <p:ajax event="change" update="cs" />
                    </p:selectOneMenu>
                </h:panelGrid>
                <p>
<h:outputText id="cs"
                                  value="#{dataBean.selectedCity} #{dataBean.selectedCity ne null ? ',': '' } #{dataBean.selectedState}"/>
                </p>
</p:panel>
        </h:form>
    </h:body>
</html>

DataBean.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.bluelotussoftware.example.jsf;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
 
/**
 * Page Backing bean for example application.
 *
 * @author John Yeary
 * @version 1.0
 */
@ManagedBean
@ViewScoped
public class DataBean implements Serializable {
 
    private static final long serialVersionUID = -1776353555799643520L;
    private List<selectitem> states;
    private Map<String, List<selectitem>> cities;
    private String selectedState;
    private String selectedCity;
 
    /**
     * Default Constructor.
     */
    public DataBean() {
        states = new ArrayList<selectitem>();
        cities = new HashMap<String, List<selectitem>>();
    }
 
    /**
     * Initializes data.
     */
    @PostConstruct
    private void initialize() {
        states.add(new SelectItem("Maine"));
        states.add(new SelectItem("South Carolina"));
        states.add(new SelectItem("Illinois"));
 
        List<selectitem> mcities = new ArrayList<selectitem>();
        mcities.add(new SelectItem("Augusta"));
        mcities.add(new SelectItem("Bangor"));
        mcities.add(new SelectItem("Bath"));
        mcities.add(new SelectItem("Brunswick"));
        mcities.add(new SelectItem("Castine"));
        mcities.add(new SelectItem("Ellsworth"));
        mcities.add(new SelectItem("Portland"));
        mcities.add(new SelectItem("Woolwich"));
        cities.put("Maine", mcities);
 
        List<selectitem> scities = new ArrayList<selectitem>();
        scities.add(new SelectItem("Charleston"));
        scities.add(new SelectItem("Clemson"));
        scities.add(new SelectItem("Columbia"));
        scities.add(new SelectItem("Greenville"));
        scities.add(new SelectItem("Simpsonville"));
        cities.put("South Carolina", scities);
 
        List<selectitem> icities = new ArrayList<selectitem>();
        icities.add(new SelectItem("Argonne"));
        icities.add(new SelectItem("Batavia"));
        icities.add(new SelectItem("Chicago"));
        icities.add(new SelectItem("Evanston"));
        cities.put("Illinois", icities);
    }
 
    /**
     * Gets the selected city.
     *
     * @return selected city.
     */
    public String getSelectedCity() {
        return selectedCity;
    }
 
    /**
     * Sets the selected city.
     *
     * @param selectedCity city to be set.
     */
    public void setSelectedCity(String selectedCity) {
        this.selectedCity = selectedCity;
    }
 
    /**
     * Gets selected state.
     *
     * @return selected state.
     */
    public String getSelectedState() {
        return selectedState;
    }
 
    /**
     * Sets the selected state.
     *
     * @param selectedState state to be set.
     */
    public void setSelectedState(String selectedState) {
        this.selectedState = selectedState;
    }
 
    /**
     * Gets a {@code List<selectitem>} for populating state names.
     *
     * @return a list of state names.
     */
    public List<selectitem> getStates() {
        return states;
    }
 
    /**
     * Gets a {@code List<selectitem>} of cities based on the selected state.
     *
     * @return a list of cities based on selected state, or an empty list if no
     * state is selected.
     */
    public List<selectitem> getCities() {
        if (selectedState != null) {
            return cities.get(selectedState);
        } else {
            return new ArrayList<selectitem>();
        }
    }
 
    /**
     * This listener cleans up the city value if a new state is selected.
     *
     * @param event a change event when the value of the state changes.
     */
    public void stateChangeListener(ValueChangeEvent event) {
        if (event.getNewValue() != selectedState) {
            selectedCity = null;
        }
    }
}

5 comments :

marcin said...

Hello,
I got:
java.lang.NullPointerException
at com.bluelotussoftware.example.jsf.DataBean.stateChangeListener(DataBean.java:159)

I run on glassfish 3.1.2 Do you know maybe reason?
Thank you

John Yeary said...
This comment has been removed by the author.
John Yeary said...

Interestingly... the exception is being caused by the valueChangeListener="#{dataBean.stateChangeListener(event)}". If you remove the (event), it will start working.

marcin said...

Thank you:)

Unknown said...

thank you soo much man your code helped me a lot _/\_

Popular Posts