Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 66 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
66
Dung lượng
515,67 KB
Nội dung
DefiningtheDeckComponent One of the most important aspects of most nontrivial applications (especially UI type apps) is the ability to respond to events that are generated by the various components of the application, both in response to user interactions and other system components . . . . —Terry Warren, SCOUG, 1999 T his chapter expands on the blueprint for building components outlined in the previous chapter. For this chapter, we will show how to create a component that can act as an accordion, or deck, which is commonly used within applications and integrated development environ- ments (IDEs) to show and hide information, such as information about selected files in a file explorer or JSF components in a component palette. Figure 3-1 shows an expandable deck used in Microsoft’s Windows Explorer. Figure 3-1. Expandable deck used in Microsoft Windows Explorer A deckcomponent has the benefit of being stackable and of being able to store more information than the equivalent space in a traditional HTML page. From a component writer’s point of view, this type of component introduces several key areas of component design, such as handling events, rendering children, and loading external resources. 105 CHAPTER 3 ■ ■ ■ 5807ch03.qxd 1/13/06 11:06 AM Page 105 Requirements for theDeckComponentThe design of thedeckcomponent will allow a user to expose specific information that is cur- rently hidden by clicking one of the displayed decks and exposing a set of items associated with the clicked deck. These child items can be anything, including links, text, and even graphics. Thecomponent should be intelligent enough to detect an already open deck and close it before opening the one requested by the user. From an application developer’s point of view, the com- ponent needs to be extensible, meaning the application developer can add as many decks as needed and include any number of children within these decks. The application developer should also be able to add any number of deck groups to a page. TheDeckComponent As you remember from the first chapter, the only reason for creating new behavioral super- classes is if the behavior and the definition have not been introduced before. According to the requirements in the previous section, thedeckcomponent should be able to selectively show nested components or groups of components, based on the user selection, and only one group will be shown at any time. To achieve this, you have to create a new Renderer to handle the selective display and a new event type to handle the user selection with an accompanying listener interface for that particular event type. Since the behavior of showing and hiding chil- dren has not been introduced yet, we will cover two new behavioral superclasses to handle the show-one-item behavior (see Table 2-1 in Chapter 2). After completing this chapter, you should understand the JSF event model and know how to create new behavioral superclasses and your own event type with a corresponding listener interface. Figure 3-2 shows the 11 classes you will create in this chapter. Figure 3-2. Class diagram showing classes created in this chapter CHAPTER 3 ■ DEFININGTHEDECK COMPONENT106 5807ch03.qxd 1/13/06 11:06 AM Page 106 The classes are as follows: • The ProShowOneDeckTag class represents the ProShowOneDeck component. • The ShowItemTag class represents leaf nodes of thedeck component. • The ShowListenerTag class represents a custom action that the application developer will use to register a ShowListener instance to a UIShowOne component. • The HtmlShowOneDeckRenderer is the new custom Renderer, which is in charge of the markup rendered to the client. • The ShowListener is a Listener interface. • The ShowAdapter supports adding a MethodBinding as a ShowListener. • The ShowEvent is the custom event class. • The UIShowItem is a behavioral superclass and represents each of the child components to the UIShowOne component. • The ShowSource class isolates the event listener management methods. • The UIShowOne class is a behavioral superclass that acts as a top-level container, controlling which one of its child components to display when activated. • And finally, the ProShowOneDeck class is your renderer-specific subclass. Designing theDeckComponent Using a Blueprint When you design a component that requires a new behavior or new functionality, it is wise to start implementing this before creating the actual Renderer for this behavior, and as such, these two steps precede the client-specific Renderer step in the blueprint, as shown in Table 3-1. Table 3-1. Steps in the Blueprint for Creating a New JSF Component # Step Description 1 Creating a UI prototype Create a prototype of the UI and intended behavior for your component using the appropriate markup. 2 Creating events and listeners (Optional) Create custom events and listeners in case your specific needs are not covered by the JSF specification. 3 Creating a behavioral superclass (Optional) If thecomponent behavior is not to be found, create a new behavioral superclass (for example, UIShowOne). 4 Creating a client-specific Renderer Create the Renderer you need that will write out the client-side markup for your JSF com- ponent. 5 Creating a renderer-specific subclass (Optional) Create a renderer-specific subclass. Although this is an optional step, it is good practice to implement it. Continued CHAPTER 3 ■ DEFININGTHEDECKCOMPONENT 107 5807ch03.qxd 1/13/06 11:06 AM Page 107 Table 3-1. Continued # Step Description 6 Registering a UIComponent and Renderer Register your new UIComponent and Renderer in the faces-config.xml file. 7 Creating a JSP tag handler and TLD This step is needed in case you are using JSP as your default view handler. An alternative solution is to use Facelets (http://facelets. dev.java.net/). As you can see, the blueprint has two additional steps: creating events and listeners and creating a behavioral superclass. According to the blueprint, you still need to first implement thecomponent in the intended markup. Step 1: Creating a UI Prototype Let’s take a moment to reflect on what you want to achieve and create a prototype of the intended markup needed for the client (in this case, a web browser). Remember, by doing so, you will find out what elements the Renderer has to generate, what renderer-specific attributes the application developer will need, and what behavior is expected to build an application with thedeck component. Figure 3-3 shows the end result of thedeckcomponent implemented in HTML. Figure 3-3. Thedeck component, implemented in HTML, showing the Java item expanded Let’s first focus on the presentation of the prototype. As you can see in Figure 3-3, thedeck has three labels—Java, Open Source, and .NET. Each label represents an expandable region, and in Figure 3-3 the Java region is currently expanded and shows its content. These labels are containers, since they can hold more than just text (for example, a combination of CHAPTER 3 ■ DEFININGTHEDECK COMPONENT108 5807ch03.qxd 1/13/06 11:06 AM Page 108 images and text). Within the expanded Java region is a mix of plain text and links. Styles con- trol the actual look and feel. Code Sample 3-1 shows the HTML needed to create thedeck component. Code Sample 3-1. TheDeck HTML Prototype Implementation <html> <head> <title>Pro JSF : ProShowOneDeck Prototype</title> <style type="text/css" > .ProShowOne { . } .ProShowItem { . } .ProShowItemHeader { . } .ProShowItemContent { . } </style> </head> <body> <div style="width:200px;" > <div class="ProShowOne"> <div class="ProShowItem"> <div class="ProShowItemHeader" onclick="alert('first')" > <img src="resources/java_small.jpg" alt="The Duke" style="margin-right: 8px; vertical-align:bottom;" /> Java </div> <div class="ProShowItemContent"> <table> <tbody> <tr> <td> <a href="http://www.apress.com/ ."> Pro JSF: Building Rich Internet Components </a> </td> </tr> <tr> <td>Pro EJB 3</td> </tr> <tr> <td>Pro Apache Maven</td> </tr> </tbody> </table> </div> CHAPTER 3 ■ DEFININGTHEDECKCOMPONENT 109 5807ch03.qxd 1/13/06 11:06 AM Page 109 </div> <div class="ProShowItem"> <div class="ProShowItemHeader" onclick="alert('second')" > Open Source </div> </div> <div class="ProShowItem"> <div class="ProShowItemHeader" onclick="alert('third')"> .NET </div> </div> </div> </div> </body> </html> As you can see, <div .> elements represent the label containers and their contents. The reason for choosing <div> elements instead of anchor elements (<a href>) is so you can more easily control the look and feel of thedeck nodes. If you implemented this using anchor ele- ments, you would have to deal with browser-specific behaviors to handle links, such as the look of visited links, not visited links, and so on. Apart from the obvious visual aspect, you do not need to identify which label the user has activated, since only one node can be expanded at any time. In the prototype in Code Sample 3-1, we have simulated this behavior by adding an alert (for example, onclick= "alert('first')") to the <div> element representing the label of the expandable region. By examining the HTML source in Code Sample 3-1, you can also see that you need to expose attributes for four style classes—ProShowOne, ProShowItem, ProShowItemHeader, and ProShowItemContent. Code Sample 3-2 show how to map some of the visible HTML attributes to their corresponding UIComponent attributes. Code Sample 3-2. Parameterized HTML for the showOneDeck Renderer <div class=[showOne.styleClass]> <div class=[showOne.itemStyleClass]> <div class=[showOne.itemHeaderStyleClass] onclick="alert([showItem.id])" > <img src="resources/java_small.jpg" alt="The Duke" style="margin-right: 8px; vertical-align:bottom;" /> Java </div> <div class=[showOne.itemContentStyleClass]> <table> <tbody> CHAPTER 3 ■ DEFININGTHEDECK COMPONENT110 5807ch03.qxd 1/13/06 11:06 AM Page 110 <tr> <td> <a href="http://www.apress.com/ ."> Pro JSF: Building Rich Internet Components </a> </td> </tr> <tr> <td>Pro EJB 3</td> </tr> <tr> <td>Pro Apache Maven</td> </tr> </tbody> </table> </div> </div> <div class="[showOne.itemStyleClass]" > class="[showOne.itemHeaderStyleClass]" onclick="alert([showItem.id])" > Open Source </div> </div> <div class="[showOne.itemStyleClass]" > <div class="[showOne.itemHeaderStyleClass]" onclick="alert([showItem.id])" > .NET </div> </div> </div> Part of the design of thecomponent is that it should allow the user to expand only one item at a time. For this you need to first identify the item activated by the user; this takes place with the alert() function attached to each item, and [showItem.id] illustrates the identifier. In addition, you need a way to keep track of each item and to ensure that only one is expanded at any time. To achieve this, you need a parent container that can listen for the event identifying the activated item and then expand it and close the previously opened item. The prototype uses the <div class=[showOne.styleClass]> element as the logical parent container. This design of having a logical container for multiple items is modeled after HtmlDataTable and UIColumn in the JSF specification. The attributes in the prototype are associated with one of these compo- nents (in other words, the parent container, showOne) or one of its children (showItem). It is important to note that although the prototype describes the user interface require- ments, some attributes and functionality still might not be visible or make sense in the actual prototype. For the HTML source in Code Sample 3-2, one attribute is not visible but still needed by the implementation—showOne.showItemId. It will be used to set the default expanded item CHAPTER 3 ■ DEFININGTHEDECKCOMPONENT 111 5807ch03.qxd 1/13/06 11:06 AM Page 111 on the initial request. Additionally, you need to let application developers listen for events on thecomponent showOne.showListener and invoke application logic when an item has been activated. Before you start creating thedeck component, take a sneak peak at the final result and how it will be used in a JSP page, as shown in Code Sample 3-3. Code Sample 3-3. DeckComponent As It Would Be Used in a JSF JSP Document <?xml version="1.0" encoding="UTF-8" ?> <jsp:root .> <jsp:directive.page contentType="text/html" /> <f:view> . <pro:showOneDeck showItemId="first" showListener="#{backingBean.doShow}" > <pro:showItem id="first" > <f:facet name="header" > <h:panelGroup> <h:graphicImage url="/resources/java_small.jpg" alt="The Duke" style="margin-right: 8px; vertical-align:bottom;" /> <h:outputText value="Java" /> </h:panelGroup> </f:facet> <h:panelGrid columns="1" > <h:outputLink value="http://www.apress.com" > <h:outputText value="Pro JSF: Building Rich Internet Components" /> </h:outputLink> <h:outputText value="Pro EJB 3" /> <h:outputText value="Pro Apache Maven" /> </h:panelGrid> </pro:showItem> <pro:showItem id="second" > <f:facet name="header"> <h:outputText value="Open Source" /> </f:facet> <h:panelGrid columns="1" > <h:outputText value="Foundations of AJAX" /> <h:outputText value="Pro Apache Ant" /> <h:outputText value="Pro PHP Security" /> </h:panelGrid> </pro:showItem> <pro:showItem id="third" > <f:facet name="header"> CHAPTER 3 ■ DEFININGTHEDECK COMPONENT112 5807ch03.qxd 1/13/06 11:06 AM Page 112 <h:outputText value=".NET" /> </f:facet> <h:panelGrid columns="1" > <h:outputText value="Pro .NET Extreme Programming" /> <h:outputText value=".NET for Delphi Programmers" /> </h:panelGrid> </pro:showItem> <pro:showListener type="com.apress.projsf.ch3.application.MyShowListener" /> </pro:showOneDeck> . </f:view> </jsp:root> The tags highlighted in bold represent the JSF components you will learn how to create in this chapter. As you can see, the sample is a fairly simple application with one parent component—<pro:showOneDeck . >—that keeps track of which item is currently open and which node is set to be expanded by default. In the page the parent component has three children—<pro:showItem . >. Each <pro:showItem . > child component has its own unique identifier (for example, first, second, and third). Each <pro:showItem . > has a facet—<f:facet name="header">—associated with it representing the “header” of the click- able area of the item (see Chapter 1 for more about facets). Part of thedeck component’s requirements is to allow application developers to use any component to represent the actual clickable header, and as examples we have used regular <h:outputText> and <h:panelGroup> components. Nested within each <pro:showItem . > is a set of children, which will be displayed when the user selects an item. When the user selects any of the <pro:showItem . > components, an event will be delivered to the event queue for processing in the Invoke Application phase. To be able to react to this event, a new listener—<pro:showListener . />—listens for the aforementioned event. Step 2: Creating Events and Listeners To be able to create the component, you need to understand two new behavioral superclasses— UIShowOne and UIShowItem. The UIShowOne behavioral superclass keeps track of which node the user has selected, and the UIShowItem acts as a clickable parent container that will either show or hide its children. For these new UIComponents, you also need a new event type, ShowEvent, with a corresponding event listener interface, ShowListener, to notify application developers and to attach application code to the component. The new event instance needs to keep track of which item the user has selected. On top of this, you need to create a new Renderer to han- dle the selective rendering with accompanying renderer-specific subclasses and JSP tag handlers. Figure 3-4 shows the classes needed for the event and listener implementation that you will learn how to create in this chapter. CHAPTER 3 ■ DEFININGTHEDECKCOMPONENT 113 5807ch03.qxd 1/13/06 11:06 AM Page 113 Figure 3-4. Class diagram showing all classes needed for the event and listener implementation Event Handling Overview This section will cover a few topics regarding the JSF event model before you see the code for the event and listener implementation for thedeck component. If you have experience developing applications with the Swing toolkit or Oracle’s ADF Swing framework, you will notice that the event model implemented by JSF is similar. In fact, JSF implements a model for event notification and listener registration based on the naming convention in the JavaBeans specification, version 1.0.1. Essentially, this means an application developer can write application code and register it to listen for a specific event. A UIComponent delivers the event itself (for example, when a user clicks a button, which is similar to the approach taken in other UI toolkits). Application developers will immediately recognize the benefits of such a model, since it has proven to be easy to maintain and develop. It allows appli- cation developers to write application code for specific events in well-defined blocks of code like the ones used in Microsoft Visual Basic. The main difference between the Swing framework and JSF is that Swing operates in a stateful mode and is always listening for events fired by the client; by contrast, JSF works in a stateless environment. With no permanent connection between the client and the backend server, JSF cannot always listen to events and has to rely on postbacks to be notified about any changes on the client that might cause an event to be delivered. This limitation of HTTP has CHAPTER 3 ■ DEFININGTHEDECK COMPONENT114 5807ch03.qxd 1/13/06 11:06 AM Page 114 [...]... UIComponent in thecomponent hierarchy When the Renderer for a component discovers that the user has triggered an event, thecomponent s Renderer creates an instance of the corresponding FacesEvent subclass and queues the event to the source component 125 5807ch03.qxd 11:06 AM Page 126 CHAPTER 3 s DEFINING THE DECK COMPONENT For example, when the Renderer for the UIShowOne component discovers that the. .. processDecodes() on the currently active UIShowItem child component (if any) and then call the decode() method on the UIShowOne component itself If a Renderer is present for the UIShowOne component, the decode() method delegates to the Renderer The UIShowItem Behavioral Superclass The UIShowItem component is needed to allow the application developer to add labeled items to the deck component The UIShowItem component. .. UIShowOne() { } /** * Returns thecomponent family for this component * * @return thecomponent family */ public String getFamily() { return COMPONENT_ FAMILY; } The UIComponent and UIComponentBase classes are the foundation of all JSF components, and they define the behavioral contract and state information for all components The UIComponentBase class (javax.faces .component. UIComponentBase) is a convenience... in the deck component from the event instance and prints them to the system log window Event Handling in the JSF Lifecycle When a user interacts with the deck component (for example, expanding an item), a request is sent to the server with information about the action performed By now you know that the first phase, Restore View, will restore thecomponent hierarchy on postback The second phase is the. .. trigger a postback to the FacesServlet by submitting the form Code Sample 3-20 The Source of the showOneDeck.js File /** * The onclick handler for HtmlShowOneDeckRenderer * * @param formClientId the clientId of the enclosing UIForm component * @param clientId the clientId of the ProShowOneDeck component * @param itemId the id of the UIShowItem that was clicked */ function _showOneDeck_click( formClientId,... DEFININGTHEDECKCOMPONENT _transient = isTransient; } private MethodBinding _showMethod; private boolean _transient; } The ShowAdapter implements the processShow method, calling the specified MethodBinding with the ShowEvent parameter It is important that the MethodBinding passed to the ShowAdapter constructor matches the signature of the processShow method Therefore, the ProShowOneDeckTag uses the. .. HtmlShowOneDeck component, you ensure you expand the correct HtmlShowOneDeck component You first get the form—document.forms[formClientId] Then, knowing the form, you can access the hidden input field, if it exists, and set the clientId of the selected UIShowItem component input.value = itemId You finish the function by submitting the form and passing the new values to the server-side component hierarchy... methods of the UIComponent class The UIShowOne class extends the UIComponentBase class, which is recommended since it will protect the UIComponent subclass— UIShowOne—from any changes to the signature of the UIComponent implementation that might occur in the future The ShowSource interface is implemented to make sure you comply with the rules for which custom listeners can be attached to the component. .. Renderer for the particular UIComponent Thecomponent type returned by the getComponentType() method is a string that is used by the Application object as an identifier for the UIComponent subclass (for example, UIShowOne) Following the naming convention from the previous chapters, thecomponent family and component type are both called com.apress.projsf.ShowOne Code Sample 3-12 introduces the first behavioral... name for the UIShowOne component s Renderer is com.apress.projsf.ch3.render.html.basic HtmlUIShowOneDeckRenderer The HtmlShowOneDeckRenderer Class Figure 3-10 shows the HtmlShowOneDeckRenderer extending the HtmlRenderer introduced in Chapter 2 5807ch03.qxd 1/13/06 11:06 AM Page 137 CHAPTER 3 s DEFINING THE DECK COMPONENT Figure 3-10 Class diagram showing the HtmlShowOneDeckRenderer extending the HtmlRenderer . for the Deck Component The design of the deck component will allow a user to expose specific information that is cur- rently hidden by clicking one of the. The classes are as follows: • The ProShowOneDeckTag class represents the ProShowOneDeck component. • The ShowItemTag class represents leaf nodes of the