<< back to articles

Getting Struts' <html:optionsCollection> Tag to Work for You

Version 1.1 of the struts-html tag library comes with a new tag, <html:optionsCollection>, which simplifies the process of setting the domain of values associated with an HTML <SELECT> tag (i.e., its list of <OPTION> tags) to the members in a Collection object defined in the form page's corresponding form-bean object. Although this new tag was intended to simplify this process for developers, as is often the case, the documentation on its usage is sparse and confusing. This article seeks to enlighten developers on how they can make use of this new tag to simplify their population of dropdown fields in HTML forms used within a Struts application.

The struts-html JSP tag library that is part of the Struts framework distribution contains many useful tags for populating the contents of form fields on HTML form pages and perpetuating these contents across requests automatically. This becomes critical when filling out form pages that require validation. In this scenario, a user fills out a form, and submits the form to a servlet (e.g., the Struts ActionServlet) that directs processing to a Struts Action that requests validation of the entered values.

If the values are invalid (e.g., fields that should not be blank have been left blank, or fields that should have numeric content only have alphabetic characters, or the entered value may simply have been too long), the application re-presents the form to the user. However, when the form is presented again to the user, it should not be blank—its fields should be filled in with whatever values the user entered, including the invalid ones. (The user may have simply mistyped and entered an invalid character, while the bulk of the text is a field was valid. In this situation, the user should not be forced to type in the entire contents of a field from scratch.)

This is accomplished in Struts by replacing "normal" HTML tags with struts-html JSP tag equivalents. For instance, the HTML <FORM> tag is replaced with the <html:form> JSP tag. Elements within the form are similarly replaced: <INPUT TYPE="text"> tags are replaced with <html:text>, <INPUT TYPE="radio"> tags are replaced with <html:radio>, <INPUT TYPE="submit"> tags are replaced with <html:submit>, etc. The JSP tag equivalents are translated into their HTML counterparts when the page is displayed, and the values associated with those tags (be they textual entries or statuses like "CHECKED" or "SELECTED") are set as well. Thus, when you check a particular checkbox or radio button and submit the form, if the form needs to be displayed to you again, the checkbox or radio button will still be checked. Consider this part of the server-side "magic" that Struts and its associated tag library performs for your application.

Form page with struts-html tags

<html:form action="/submitForm" method="get">
<b>First Name:</b> <html:text property="firstName" length="30" />
<b>Last Name:</b>  <html:text property="lastName"  length="30" />
<b>Favorite Color:</b>
<br><html:radio property="color" value="1"> blue
<br><html:radio property="color" value="2"> green
<br><html:radio property="color" value="3"> yellow
<br><html:radio property="color" value="4"> red
</html:form>

Translated into HTML

<FORM ACTION="/submitForm" METHOD="get">
<b>First Name:</b> <INPUT TYPE="text" NAME="firstName" LENGTH="30">
<b>Last Name:</b>  <INPUT TYPE="text" NAME="lastName"  LENGTH="30">
<b>Favorite Color:</b>
<br><INPUT TYPE="radio" NAME="color" VALUE="1"> blue
<br><INPUT TYPE="radio" NAME="color" VALUE="2"> green
<br><INPUT TYPE="radio" NAME="color" VALUE="3"> yellow
<br><INPUT TYPE="radio" NAME="color" VALUE="4"> red
</FORM>

Submitted Request URL (with Query String)

http://.../submitForm?firstName=Ted&lastName=Nugent&color=3

The HTML <SELECT> tag provides a more complicated scenario. This tag contains multiple <OPTION> tags, each of which contains a value (and potentially a distinct label for each value as well). The user can select one of these options (or more than one if the <SELECT> tag is coded with the MULTIPLE parameter), and the value(s) associated with the selected option(s) is/are included in the request transmitted as part of form submission.

<FORM ACTION="/submitForm" METHOD="get">
    ...
<b>Country:</b>
<SELECT NAME="countryCode" MULTIPLE>
    <OPTION VALUE="US">United States</OPTION>
    <OPTION VALUE="UK">United Kingdom</OPTION>
    <OPTION VALUE="FR">France</OPTION>
    <OPTION VALUE="DE">Germany</OPTION>
    ...
</SELECT>
</FORM>

It might be reasonable in some circumstances to expect that the list of options would be hardcoded in the page, with a series of <html:option> tags included verbatim with specific labels and values. The Struts application would know to added a "SELECTED" parameter to the option(s) that was selected when the form was submitted.

<html:form action="/submitForm" method="get">
    ...
<b>Country:</b>
<html:select property="countryCode" multiple>
    <html:option value="US">United States</html:option>
    <html:option value="UK">United Kingdom</html:option>
    <html:option value="FR">France</html:option>
    <html:option value="DE">Germany</html:option>
    ...
</html:select>
</html:form>

In more complicated circumstances, though, the list of options is likely to be derived dynamically on the server side: either by a database query or the specification of a static domain (like countries and states/provinces). In earlier versions of Struts, this was accomplished using the <html:options> tag, but with the advent of Struts 1.1 there is a new tag, <html:optionsCollection>, that associates the domain of an HTML <SELECT> tag with a Collection object defined on the server side in a more flexible and convenient manner.

Struts Form-Bean Review

Before we proceed further, it is a good idea to confirm our understanding of how Struts form-beans work. A form-bean class is associated with a form page as part of the Struts configuration file, struts-config.xml.

<form-bean name="customSubmitForm"
    type="com.yourcompany.appname.struts.form.CustomSubmitForm" />

<action path="/submit"
    type="com.yourcompany.appname.struts.action.FormSubmitAction"
    name="customSubmitForm" scope="request"
    input="failure" validate="true">
        <forward name="failure" path="/pages/submitForm.jsp" />
</action>

In the example above, the form-bean named "customSubmitForm" is associated with the Java class com.yourcompany.appname.struts.form.CustomSubmitForm. The "/submitForm" URL, in turn is associated with a specific action class (com.yourcompany.appname.struts.action.FormSubmitAction) and the name of a specific form-bean (the previously defined "customSubmitForm"). These associations imply that when a form is submitted to URL http://.../submitForm?..., the form-bean class that will be used is com.yourcompany.appname.struts.form.CustomSubmitForm. In order to make all of this "magic" come together, the form-bean class must contain getter and setter methods for all the fields in the form, so that the field values can be repopulated in the HTML tags that are presented if the form page is displayed again. To support the "firstName" field, there must be a getFirstName() method and a setFirstName(String firstName) method.

Likewise, to support the "country" field, there must be a getCountry() method and a setCountry(String country) method. That's all well and good, but it does not help populate the domain of possible option values associated with the country field. Again, hardcoding <html:option> is an option (no pun intended), but it becomes tedious and nearly impossible when dealing with large domain sets or domains that are derived dynamically.

Enter the <html:optionsCollection> tag.

<html:select property="countryCode" multiple>
    <html:optionsCollection property="countryOptions" />
</html:select>

What's required to make this work is one more getter method in the form-bean class—in this case, getCountryOptions()—that is invoked to populate the option list for this <SELECT> tag.

Construct the Getter Method for the Domain Collection Property

This getter method must return a Collection object that contains the values and labels associated with the list of countries. As we can see from the earlier example, we would like the labels to be the full country names, while the values should be the two-letter country codes, e.g., "US" for "United States", "DE" for "Germany" (Deutschland).

This is where it starts to get confusing: what kinds of objects should this Collection contain? How should they be constructed and accessed? How do we distinguish between the label and the value for an option?

In a nutshell, the answer is intuitive (once it's been explained): the Collection should contain JavaBeans that have two properties, label and value. Thus these objects require (at a bare minimum) a getLabel() method and a getValue() method.

Fortunately, there is a Struts utility class, org.apache.struts.util.LabelValueBean, that not so coincidentally fulfills these very requirements by implementing these very methods. All that needs to be done, then, is to construct a Collection object (you will probably want a List, so that the entries are displayed in the order they are entered) and add LabelValueBeans for each entry you desire. The <html:optionsCollection> tag will:

  1. iterate over the elements of the retrieved Collection,
  2. invoke the getValue() and getLabel() on each element, and
  3. display each of the elements as an HTML <OPTION> tag:
    <OPTION VALUE="value">label</OPTION>

public class CustomSubmitForm extends ActionForm {

    private String m_firstName   = null ;
    private String m_countryCode = null ;

    private List m_countryOptions = null;
    
    public String getFirstName() {
        return m_firstName ;
    }
    
    public String getCountryCode() {
        return m_firstName ;
    }

    public Collection getCountryOptions() {
        if (m_countryOptions == null) {
            m_countryOptions = new Vector(200) ;
            m_countryOptions.add(
                new LabelValueBean("United States","US")) ;
            m_countryOptions.add(
                new LabelValueBean("Germany","DE")) ;
            ...
        }
        return m_countryOptions ;
    }
}

This manual approach is reasonable for small static domains. But what if the list of countries your company does business with changes on a regular basis? You certainly would not want to hard-code the list of countries in your pages; this would require you to update all of the pages when this list changes. But likewise, you would also not want to hard-code the list in your Java code!

With this in mind, you might want to have your getCountryOptions method get its list via a database query. To facilitate this process in our own application, and to add flexibility in the display of options lists, we created our own class that extends LabelValueBean: the OptionsCollectionElement class. It differs from LabelValueBean in the following ways:

  1. Its basic constructor takes the value as the first argument and the label as the second. This is the reverse of the order used in the LabelValueBean class, but it seemed more intuitive since the value is the more critical of the two components in this class.
  2. We created a second constructor that takes one argument, the value, which is used as both value and label.
  3. We included two static Comparators within the class as constants: SORT_BY_VALUE and SORT_BY_LABEL. These Comparators let you use a SortedSet (e.g., a TreeSet) to hold your Collection. This means that the values need not be inserted in the order you want them presented: the presentation is sorted either by label or by value.

Here is an example of a getCountryOptions() method that uses the OptionsCollectionElement class. Note the following:

  1. that this method invokes a Data Access Object (DAO), namely the DataAccessService class, to process database queries, and
  2. that in our application, we took this method of out of the form-bean class and placed it in a common DomainService class, since multiple form pages are likely to want a list of countries.

public Collection getCountryOptions() {
    Collection c = new TreeSet(OptionsCollectionElement.SORT_BY_LABEL) ;
    try {
        ResultSet rs =
            DataAccessService.getInstance().executeQuery(
                "SELECT * FROM VRLS_XREF_COUNTRY") ;
        while (rs.next()) {
            String value = rs.getString("COUNTRY_CODE") ;
            String label = rs.getString("COUNTRY_NAME") ;
            OptionsCollectionElement oce =
            	new OptionsCollectionElement(value, label) ;
            c.add(oce) ;
        }
    }
    catch (Exception e) {
        LoggingService.getInstance().error(
            "Could not retrieve countries option list: " + e) ;
    }
    
    return c ;
}

This method could have employed the SORT_BY_VALUE static Comparator, if you wanted to sort your list by country code rather than country name. If you want the flexibility to decide at the last minute whether to sort by country code or country name, you could create two methods: one called getCountryOptionsByLabel and the other called getCountryOptionsByValue. You could then choose to put either property="countryOptionsByLabel" or property="countryOptionsByValue" in the <html:optionsCollection> tag on your JSP page, e.g.:

<html:select property="countryCode">
	<html:optionsCollection property="countryOptionsBy_____">
</html:select>

As with so many features in open source software, the functionality is magnificent but the documentation is lacking or confusing. This new <html:optionsCollection> tag greatly simplifies the process of populating option lists for HTML <SELECT> tags, provided you understand how to use it. We hope this article proved enlightening in explaining how this tag works.