Attributes are used to pass information into a custom tag to configure the behavior of the tag. Tag attributes are like XML or HTML attributes and are name/value pairs of data. The values must be quoted with single or double quotes.
A simple example might be a tag that lists the rows in a table given a JNDI DataSource and table name as attributes. Listing 14.6 shows how such a tag could be used.
The JSP specification allows attributes to use the Expression Language. In Listing 14.6 the dataSource attribute uses a simple string value whereas the table attribute uses a request time expression.
The TLD description for the tag must define any attributes it uses. Each supported attribute name must be listed together with details of the attribute. Every attribute must have an <attribute> tag with the sub-components shown in Table 14.6.
Tag | Description |
---|---|
attribute | Introduces an attribute definition in the <tag> component of the TLD. |
name | Defines the attribute name. |
required | Followed by true if the attribute must be provided; otherwise, false. |
rtexprvalue | Defines whether the attribute can be specified with a request time expression. Set this element to true to allow EL in the value of the attribute; otherwise, the value must be a string literal. |
type | Defines the type of an attribute and defaults to java.lang.String. This element must be defined if the rtexprvalue is true and the attribute value is not a String. |
Listing 14.7 shows the complete TLD entry for the lookup tag.
For a tag to support attributes, it must follow the JavaBean idiom of providing get and set methods for manipulating the attributes as shown in Listing 14.8.
The set method for each attribute specified for the tag is called prior to the doStartTag() method. The doStartTag() and other tag lifecycle methods can use the values of attributes to modify their behavior.
The lookup tag is included the examples.war file on the accompanying Web site and, after deploying the examples, can be viewed using the URL:
http://localhost:8000/examples/lookup
Note how the JSP and tag combine to suppress the table listing section of the returned Web page if no table name is specified as an HTTP request parameter.
Now that you have seen how to write simple tags, you can use them to clean up complex functionality in your applications. Consider the skills.jsf page you were shown yesterday, which displayed a list of selected skills in an HTML <SELECT> statement asfollows:
<SELECT name="skills" multiple size="6"> <% Iterator allSkills = agency.getSkills().iterator(); while (allSkills.hasNext()) { String s = (String)allSkills.next(); boolean found = false; for (int si=0; !found && si<skills.length; si++) found = s.equals(skills[si]); if (found) out.print("<OPTION selected>"); else out.print("<OPTION>"); out.print(s); out.print("</OPTION>"); } %> </SELECT>
Using just the JSTL you can refactor the job list as follows:
<SELECT name="skills" multiple size="6"> <c:forEach var="agencySkill" items="${agency.skills}" > <c:set var="skillFound" value="false"/> <c:forEach var="jobSkill" items="${job.skills}" > <c:if test="${jobSkill == agencySkill}" > <c:set var="skillFound" value="true"/> </c:if> </c:forEach> <c:choose> <c:when test="${skillFound}" > <OPTION selected>${agencySkill} </c:when> <c:otherwise> <OPTION>${agencySkill}</OPTION> </c:otherwise> </c:choose> </c:forEach> </SELECT>
This is not much better than the original JSP version with embedded Java; in fact, you might think that the restricted syntax of JSTL makes this worse than the original. A custom tag can be used to simplify the code, as follows:
<SELECT name="skills" multiple size="6"> <c:forEach var="agencySkill" items="${agency.skills}" > <agency:option option="${agencySkill}" selected="${job.skills}"/> </c:forEach> </SELECT>
The custom tag here is called <agency:option> and takes two attributes:
option is the value of the current HTML option tag.
selected is a list of selected options so that the tag can flag whether the <OPTION> is SELECTED or not.
Emulating the style of the items attribute of the JSTL forEach action, this tag handles collections, arrays, and even scalar Java objects for the selected attribute by accepting a java.lang.Object as the attribute type. This is shown in the following TLD entry for this tag:
<tag> <name>option</name> <tag-class>web.OptionTag</tag-class> <body-content>empty</body-content> <attribute> <name>selected</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.lang.Object</type> </attribute> <attribute> <name>option</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>java.lang.String</type> </attribute> </tag>
Implementing the tag requires a bit of thought on how to handle the different types of parameter that can be given for the selected attribute. Listing 14.9 shows the actual tag.
The complexity in the tag shown in Listing 14.9 is in handling the different possible Java objects that can be passed in as the selected attribute. The doStartTag() method uses the helper method selectedIterator() to obtain an iterator for the list of selected options and uses this to determine whether to output <OPTION> or <OPTION SELECTED> before the toString() value of the object. The selectedIterator() method returns the iterator of a collection or creates an object of the nested static class ArrayIterator to create an iterator for an array object.
Writing custom tags like the <agency:option> tag to simplify the HTML tags is such an obvious technique that several tag libraries already do this. Don't rush off and write your own custom tags to replace HTML tags but use those already provided. Both the JavaServer Faces (http://java.sun.com/j2ee/javaserverfaces) and Apache Struts (http://jakarta.apache.org/struts) implementations include custom tags for many of the HTML tags.
There are other simple improvements you can make to the Agency case study if you know how to declare EL variables from within a custom tag, as described in the next section.
18.191.168.203