Getting the Most Out of the Struts Tag Libraries
Pages: 1, 2
Collections, Maps, and the Iterate Tag
There are often times within a Struts application that you must display a collection of objects to the user. The scenario usually goes something like this: for each item in the collection, display one or more attributes from the object. For example, in a page that renders a classic shopping cart, each Object represents one item that has been added to the cart. Figure 1 shows an example of a shopping cart page.
Figure 1. A Shopping Cart page showing multiple items
Using JSP without Struts, this functionality would most likely be accomplished with some rather unattractive looking Java Scriptlets using loops. With the Struts Iterate Tag, it becomes somewhat innocuous and much easier to manage over the lifetime of the application.
The following example code snippet shows what this might look like using the Iterate Tag from the Logic library.
Example 1. Logic Iterate Code for Shopping Cart in Figure 1
<logic:iterate id="cartItem" name="UserContainer" property="cart.items"> <!-This Table Row will be repeated for each item in the user's cart --> <tr bgcolor="#ffffff"> <!--Delete checkbox for item --> <td valign="top" align="middle"> <input type="checkbox" value="<bean:write name="cartItem" property="id"/>" name="deleteCartItem"> </td> <!-- name and description for item --> <td valign=top align=left> <html:link page="/action/viewitemdetail" paramName="cartItem" paramId="id" paramProperty="id"> <!-- Write out the name of the Item --> <bean:write name="cartItem" property="name"/> </html:link> <br>In Stock - Usually ships in 2-3 business days<br> Available for pickup at most stores </td> <!-- Write out a Quantity Input Field --> <td valign="top" noWrap align="middle"> <input type="text" maxlength="3" size="3" name="qty_<bean:write name="cartItem" property="id"/>" value="<bean:write name="cartItem" property="quantity"/>"> </td> <!-- Unit price for item --> <td valign="top" noWrap align="middle"> $<bean:write name="cartItem" format="#,##0.00" property="unitPrice"/> </td> <!-- Extended price for item --> <td valign="top" align="right"> <table cellspacing="2" cellpadding="3" width="100%" border="0"> <tbody> <tr> <td align="right" bgcolor="#ffcc00"> $<bean:write name="cartItem" format="#,##0.00" property="extendedPrice"/> </td> </tr> </tbody> </table> </td> </tr> </logic:iterate>
Looking at this snippet from a JSP, you might be inclined to comment on how formidable it appears. You should however quickly recognize that with the exception of a few Struts tags, most of the text is HTML; which is how it should be.
Using collections is rather straightforward. However, if
your data is for some reason stored within an instance of a
java.util.Map, getting at the properties in the map is a
little trickier, but nonetheless solvable with the Iterate
Tag. Using the Iterate Tag, we can iterate through the Map
and access each element. Normally each Object that you
iterate over is an element of the underlying collection, a
ShoppingCartBean, for example. However, when iterating over a
Map instance, the Object is of type
As the Struts documentation describes,
Map.Entry has two
- Key: The key under which the item is stored in the Map.
- Value: The value associated with the key.
Let's say you are using a Struts ActionForm (standard
or a dynamic) and it contains a property named "usermap"
that is declared as a Map and instantiated as a
java.util.HashMap. In this Map, the key is a unique user ID
and the value is a String representing the users encrypted
password. Figure 2 illustrates what this would look like.
Figure 2. Data Structure for Example 2
The following code fragment shows an example of iterating through this Map and writing out the two values.
Example 2. Iterate Tag used with a Map
<logic:iterate id="user" name="userForm" property="usermap"> <tr> <td>User Id: <bean:write name=" user" property="key"/></td> <td>Password: <bean:write name=" user" property="value"/></td> </tr> </logic:iterate>
As you can see from this JSP fragment, using the Iterate Tag with Maps is not much different than with Collections. The primary distinction is that of using the key and value attributes to access the data stored within the Map. Once you have references to this data, you can use it within the Iterate block just as you would with anything else.
Formatting Errors with the Errors Tag
Let's face it, as hard as you try to make GUIs user-friendly and bulletproof, end users make mistakes. One of the most common mistakes is incorrect data entry. Your applications need to be very careful when accepting input from users in order to ensure the integrity of the datastore is maintained; Struts applications are no exception. Data entered from the browser must be validated and if errors are found, information needs to be displayed to the user describing what the problems are and what steps to take next.
The Struts framework includes the ability to catch errors
and populate an Errors collection that can be accessed from
within the JSPs. This is made even easier when using the
Validator framework. Whether you're doing programmatic or
declarative validation, the Errors collection is inserted
into the request and available to a JSP. You can use either
Messages or the
(depending on which type of
validation problems occurred) to access the messages to
display to the users.
Both of the
support an "id"
attribute that is used to store the current error while
looping through the collection of problems. In a very simple
approach you can use the following within your JSP:
<html:messages id="error"> <li><bean:write name="error"/></li> </html:messages>
This fragment would print out each message from the problem collection inside an HTML list.
There's a header and footer entry that you can pull from the
resource bundle, which allows you to provide some text to
the beginning and end of the errors respectively. The
following fragment shows the usage of the
<bean:message key="errors.header"/> <ul> <html:messages id="error"> <li><bean:write name="error"/></li> </html:messages> </ul><hr>
The resource bundle would need to contain the
errors.header=<h3><font color="red">Validation Error</font></h3>You must correct the following error(s) before proceeding.
If the message collection is empty however, you would still see the header text and therefore it's best to check for the presence of messages before printing anything out:
<logic:messagesPresent> <bean:message key="errors.header"/> <ul> <html:messages id="error"> <li><bean:write name="error"/></li> </html:messages> </ul><hr> </logic:messagesPresent>
There are equivalent Tags for the errors collection.
Dealing with Dates and Currencies
The Java language provides within its core libraries, the
ability to deal with Dates and Currencies. One of the most
sought after requirements is to format a Date or a Currency
value based on a Country or Language. Java provides the
java.util.Locale class, which encapsulates the country and
language and makes it easier for applications to understand
and deal with geographical differences.
The Struts framework makes use of this by storing the Locale for a user in the HttpSession, which is created the first time a request is received from a user. This functionality can be turned off in Struts, but it's on by default.
Once the Locale is stored in the user's session, the framework can make decisions about which localization is performed. For example, the Struts framework will chose a resource bundle that best matches the user's Locale. This makes it somewhat easier to brand a site because localized text can be rendered automatically be the framework.
Many of the Tags also support this I18N functionality. For
example, the Message Tag within the Bean Library uses the
Locale to automatically pull text from the proper bundle.
However, there are some additional ways you can format
text output. The Write Tag has a
format attribute that you
can use to format currency in a particular format. Suppose
that I wanted to display currency in the following manner:
Which is specific to mostly U.S. markets, then I could use the Write Tag this way:
<bean:write name="cartItem" format="#,##0.00" property="unitPrice"/>
Although this works, there's actually a more portable way of
doing this to support multiple formats. There's an attribute
formatKey on the Write Tag that will pull the format
from the resource bundle. The Tag entry would look like the
And the resource bundle would need to have the following entry:
<bean:write name="cartItem" format="currencyFormat" property="unitPrice"/>
Doing it this way allows each resource bundle to specify a currency format specific to its Locale.
Displaying Checkboxes with the Options Tag
One of the seemingly easiest things to do with HTML may be one of the hardest to do when building dynamic pages. That is rendering of checkboxes for an application; there are a few reasons why this is true.
First, checkboxes indicate checked/unchecked on/off, or true/false, depending on how you look at it; it's a binary
condition. In the Java world, you would think this needs to
be represented as a primitive boolean or a class
java.lang.Boolean. However, since all values are sent from
the browser to the server as Strings, this complicates
things a bit. That means that something needs to convert the
String "on" or "true" to its Boolean equivalent.
The second non-obvious thing is how browsers treat checkboxes when updating the server. If a checkbox is checked, then a value is sent to the server like name="on", where "name" is the name given to the checkbox in the HTML form. Well, guess what is sent to the server when the checkbox isn't checked? You would think name="off" or name="false", right? Actually nothing is sent. Therefore the application can have a tough time understanding what's going on with the checkboxes on the pages.
The Struts Checkbox Tag renders an HTML checkbox and will
automatically check or uncheck it depending on the state of
the Boolean value of the
ActionForm associated with the
page. You have to remember that if the checkbox is unchecked
by the user, no notification will be sent and you must take
this into account. (Hint: One of the easiest ways to think
about this is if you don't receive a value, then it must be
When you have a large number of related checkboxes, you
should consider storing the values within an array and use
Switching to the Java Standard Tag Library (JSTL)
The use of the Struts Tag libraries is a great movement forward toward smarter and faster development of web applications. However, Struts isn't cornering the market on the use of JSP Tags. Other frameworks use Tags and you don't have to use Struts to get the benefits of JSP Custom Tags.
In order to standardize some of the more useful Tags, Sun and other participants created an initiative and formalized the most commonly used Tags. This initiative was turned into JSR-52, which has now released 1.0. Java Standard Tag Library (or JSTL as it's known), provides a specification for four separate areas of concern:
- XML Processing
The specification also describes an expression language (EL) that is designed to simplify page development. As you might imagine, there is some overlap with some of the Tags provided by the Struts library. For example, looping, error handling, and conditional logic is provided by JSTL as well as Struts. The question is "which set of tags should I use?" The answer is to use the JSTL Tags where possible. For existing code, it's probably not worth the risk to change. However, for new JSPs you should be looking at the JSTL for Tag functionality. Struts 1.1 even includes a version of the JSTL Tags based on the new Expression Language. Something definitely to keep you eye on in future versions of Struts.
Chuck Cavaness is a graduate from Georgia Tech with degrees in computer engineering and computer science. He has built Java-based enterprise systems in the healthcare, banking, and B2B sectors. He is also the author of two O'Reilly books, Programming Jakarta Struts and Jakarta Struts Pocket Reference.
O'Reilly & Associates recently released (May 2003) Jakarta Struts Pocket Reference.
Sample Excerpt, Configuring Struts, is available free online.
For more information, or to order the book, click here.
Return to ONJava.com.