Designing Tapestry Mega-Componentsby Howard Ship
Tapestry: Java Web Components is a powerful, flexible, open source framework for creating Web applications in Java. Tapestry builds on existing J2EE technologies to create an environment for robust, scalable, and highly dynamic Web applications. The framework was designed from the start to allow the creation of rich, reusable components. This article will explore the construction of one such component, the Palette, which exercises many of the advanced features of Tapestry. It assumes a good deal of familiarity with Tapestry; if you haven't been exposed to Tapestry yet, the Tapestry Home Page is a good place to start.
The majority of components in Tapestry directly correspond to HTML elements. For example, the Form component generates a
<form> element, and the Image component generates an
<img> tag. There are components for input fields and links. Some components break this mold, such as the PropertySelection component, which generates a
<select> and all the
The Palette is one such mega-component. It facilitates the selection of multiple items from a list. The interface consists of two columns of values and a pair of buttons. The left column is the list of available items that may be selected. The right column is the list of items actually selected. The "select" button moves one or more items from the available list to the selected list. The "deselect" button moves items out of the selected list and back into the available list. Figure 1 shows the Palette component in action.
Here, the Palette is part of the Virtual Library application; it is being used to transfer the ownership of books from one person to another. The left column contains all the books owned by one person; the right column is the list of books to transfer to the other person. In between the two columns are the buttons for moving books back and forth between the two columns.
The Palette component includes other functionality. If desired, the two lists can be kept sorted, either by the item's label, visible to the user, or by the item's value (which is hidden from the user, but meaningful to the application). Finally, the component can be configured to allow manual sorting; in this mode, two additional buttons, "move up" and "move down," appear, allowing the user to change the order of items in the selected list.
The buttons are only active when appropriate; for example, the "select" button is disabled unless the user has selected one or more items in the available list. When disabled, the buttons take on a "dimmed" or greyed-out appearance.
Further event handling functions are needed to respond when the user presses any of the buttons. Another aspect of event handling is to enable and disable the buttons as appropriate. This includes updating the image used for each button to reflect its enabled or disabled status.
These functions will typically be put in a
<script> block that is placed between the
<body>tags. Lastly, the developer will create the HTML for the various links, images and
This same functionality could be implemented in a number of ways, using simpler HTML interfaces. In many Web applications, a single
<select> component is used for multiple selection. Although this behavior is directly supported by the Web browser, it has a number of limitations. A special modifier key must be used in conjunction with the mouse to perform multiple selections, and that modifier varies between browsers and platforms. In addition, for long lists (where the scrolling of the control is required), it is very difficult to see what exactly is selected, and easy to mistakenly lose the selection by clicking and item without pressing the modifier key.
Another option is to create a table of checkboxes and labels. This is not particularly elegant and can consume a large amount of screen space. Unlike the Palette component, a list of checkboxes requires that the user visually inspect the page to determine which items are currently selected. Neither of these simpler interfaces allows for the ordering of the selections.
Tapestry reverses those limitations. Creating the Palette component itself was involved; it is by far the most complex component yet created for Tapestry. Basic development took two days. Later, an additional day was spent reworking it to operate reasonably in Netscape Navigator 4, and then more time to adjust to Navigator 6. But now that the component exists, it is easier to use the Palette component than it would be to implement the other solutions. That is a telling demonstration of the power of using components.
Futher, there are no arbitrary limitations on using a Palette component. A developer may use as many Palettes on a page as desired, with multiple Palettes in a single form, or spread between forms.
A Palette component is represented by a single tag in its containing component's HTML template (the containing component is typically a Tapestry page, but like all Tapestry components, a Palette may be used to construct even larger, more complex components). Typically, a
<span> tag is used:
Tapestry's HTML template parser allows any HTML tag to be used to represent a Tapestry component, simply by including the
So, from this single tag, the Palette component must generate all of the following:
- HTML for the Palette itself: a table containing the pair of
<select>tags and their initial
<option>tags, as well as links for the buttons and any additional headers.
onloadevent handler, which "wires" the form elements up to their event handlers.
To pull this off, the Palette component relies on the Tapestry framework, which was designed to facilitate complex components such as the Palette.
One of the central interfaces in Tapestry is the request cycle object,
IRequestCycle. This object represents the current request invoked by the client Web browser. Every component has access to this object; it is passed to each component as one parameter of the
render() method, and it is also available as a property of the containing page.
The request cycle allows components to find other pages within the application, or to identify which page will generate the response for the current request. It also allows components to locate each other during the rendering process.
The request cycle contains a collection of named attributes (this is similar to how data is stored inside an
HttpSession). Certain components provide services to the components they wrap (the components that are, directly or indirectly, contained within the open and close tags of the component). Service-providing components can register themselves into the request cycle using a well-known name, before rendering the components they wrap. Wrapped components can find the service-providing component when their
render() method is invoked. This is shown in Figure 2.
Thus the Palette component finds the
Form and Body components because
Form and Body have registered themselves into the request cycle as part of their
Pages: 1, 2