Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 49 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
49
Dung lượng
916,35 KB
Nội dung
public InvoiceBean() { paymentTerms.add(new SelectItem(“0”, “On Receipt”, “”)); paymentTerms.add(new SelectItem(“30”, “Net 30 Days”, “”)); paymentTerms.add(new SelectItem(“60”, “Net 60 days”, “”)); } public List getPaymentTerms() { return paymentTerms; } public void setPaymentTerms(List paymentTerms) { this.paymentTerms = paymentTerms; } public String getPaymentTerm() { return paymentTerm; } public void setPaymentTerm(String paymentTerm) { this.paymentTerm = paymentTerm; } Now we can add tags to ModifyInvoice.jsp (see Listing 6.12) to render a drop-down menu based on the list of SelectItems stored in the payment- Terms property: <h:selectOneMenu id=”terms” value=”#{modifyInvoicePage.invoice.paymentTerm}”> <f:selectitems value=”#{modifyInvoicePage.invoice.paymentTerms}”/> </h:selectOneMenu> The selectOneMenu tag will render a drop-down list that is bound to the bean property referenced by the tag’s value attribute (in this case the InvoiceBean’s paymentTerm field). The options list in the resulting HTML select element will be generated from the list of SelectItems contained in the paymentTerms field identifed by the nested selectItems tag. Similarly, we could follow the same set of steps used in the previous exam- ple to add a set of radio buttons. First, here’s the new code we need to add to the InvoiceBean class: /* List of possible order status codes */ private List statusCodes = new ArrayList(); /* Current order status */ private String statusCode = “”; public InvoiceBean() { statusCodes.add(new SelectItem(“1”, “Open”, “”)); statusCodes.add(new SelectItem(“2”, “Past Due”, “”)); UI Components 219 11_462071 Ch06.qxd 5/17/04 10:17 AM Page 219 statusCodes.add(new SelectItem(“3”, “Paid”, “”)); } public List getStatusCodes() { return statusCodes; } public void setStatusCodes(List statusCodes) { this.statusCodes = statusCodes; } public String getStatusCode() { return statusCode; } public void setStatusCode(String statusCode) { this.statusCode = statusCode; } Now we can add the JSP tags: <h:selectOneRadio id=”status” value=”#{modifyInvoicePage.invoice.statusCode}”> <f:selectItems value=”#{modifyInvoicePage.invoice.statusCodes}”/> </h:selectOneRadio> As you can see, the only real difference between the drop-down list and the radio button list is the tag name, since both the selectOneRadio and the selectOneMenu tags are backed by subclasses of UISelectOne. Check boxes are a bit different. They are backed by the UISelectBoolean component, which binds its value to a single property of type boolean, rather than to a list of SelectItems. To add check boxes to a page, we simply add boolean properties to the bean class, one per check box, and then add a tag for each check box in our JSP. Here’s the additional Java code we would need to add to the InvoiceBean to support a pair of check boxes: boolean newCustomer; boolean expedited; public boolean isNewCustomer() { return newCustomer; } public void setNewCustomer(boolean newCustomer) { this.newCustomer = newCustomer; } public boolean isExpedited() { return expedited; } public void setExpedited(boolean expedited) { this.expedited = expedited; } 220 Chapter 6 11_462071 Ch06.qxd 5/17/04 10:17 AM Page 220 We could then add the following to our JSP: <h:selectBooleanCheckbox id=”expedited” value=”#{modifyInvoicePage.invoice.expedited}”> </h:selectBooleanCheckbox> <h:selectBooleanCheckbox id=”newCustomer” value=”#{modifyInvoicePage.invoice.newCustomer}”> </h:selectBooleanCheckbox> Figure 6.8 shows how the Modify Invoice page would look with the added fields: Figure 6.8 Modify Invoice page with a drop-down menu, radio buttons, and check boxes. UI Components 221 11_462071 Ch06.qxd 5/17/04 10:17 AM Page 221 Summary As we have seen, components play a central role in JavaServer Faces. They col- laborate with tags and Renderers to generate the presentation, work with Con- verters to convert values between Java types and their UI representations, assist in validation along with any registered Validators, provide a registry for listeners, and implement the mechanisms needed to assemble and manage the view. 222 Chapter 6 11_462071 Ch06.qxd 5/17/04 10:17 AM Page 222 223 In the previous chapter, “UI Components,” we learned about JSF’s rendering and value binding facilities. From an MVC perspective, our focus was primar- ily on understanding how to implement the view portion of a JSF-based Web application. Now let’s turn our attention to the controller tier. JavaServer Faces provides facilities for binding UI controls to arbitrary methods on backing beans that can be used to define how an application responds to user-initiated events. These action methods then collaborate with a built-in navigation management system that allows developers to specify navigation rules declaratively in XML. Under the covers, JSF uses an Event/Listener model, familiar to many UI developers, which allows developers to access and manipulate component state at any stage of the request/response cycle. It also provides convenient extension points for customizing the framework’s behavior. In combination, these facilities provide a straightforward foundation for building the controller layer of an MVC Model 2 Web application. Overview In this chapter, we will explore the framework’s navigation mechanism. We’ll do so by progressively developing an example application composed of sev- eral related pages—modeled on a typical, real-world business application Navigation, Actions, and Listeners CHAPTER 7 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 223 scenario. We’ll learn how to code action methods that provide the required application logic, as well as how to configure navigation rules in the applica- tion configuration file (by default, faces-config.xml) to manage the appli- cation’s page flows. We’ll also learn about how and when to implement ActionListeners and ValueChangeListeners to handle some of the subtler issues that can arise when using JSF to develop nontrivial Web applications. Actions and Navigation Implementations of JSF must provide an implementation of the Action Listener interface to serve as a default listener at the application level. When the framework generates the component tree for a given page (during Restore View or Render Response), any components of type UICommand are registered with this listener. A component that implements the ActionSource interface (for example, UICommand) will queue an ActionEvent when the framework invokes its processDecodes() method. At the end of the Invoke Application phase (or the Apply Request Values phase if the component’s immediate property is set to true), the default ActionListener is responsible for dispatching these events by evaluating the component’s method binding expression to locate an action method that it can then invoke. Action methods must take no argu- ments and return a string. The default ActionListener passes the string returned from the action method to the framework’s default NavigationHandler. The Navigation Handler in turn looks for a matching navigation rule in the application con- figuration file. If a matching value is found, the NavigationHandler calls setViewRoot() on the FacesContext with the name of the new view. If no match is found, the current view (and its tree of components) remains unchanged. At the end of the Invoke Application phase (Apply Request Values if immediate was set to true), the framework passes control to the Render Response phase, at which point the current view in the FacesContext is rendered. You can customize the framework’s behavior if desired by substituting your own implementations for the default ActionListener, the default NavigationHandler, or both if you wish, by calling setApplication Listener() or setNavigationHandler() on the application instance as necessary. 224 Chapter 7 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 224 JSF provides several features that allow you to manage navigation between your application’s pages. The simplest of these is to use an outputLink tag to render an HTML anchor tag that references a JSP page, as in the following example: <f:view> <h:outputLink id=”searchLink” styleClass=”Link” value=”Search.jsp”> <f:verbatim >Search by Account Number</f:verbatim> </h:outputLink> </f:view> JSF implementations would typically render this in HTML as follows: <a href=”Search.jsp” class=”Link”>Search by Account Number</a> While this works for simple navigation, it doesn’t provide for the dynamic behavior that many of the pages in typical Web applications require. For exam- ple, suppose that we were creating a simple search page similar to the one in Figure 7.1. Figure 7.1 A simple search page. Navigation, Actions, and Listeners 225 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 225 The Search page picture in Figure 7.1 provides a single input field into which a user may enter an account number, and a button to submit the enclos- ing form. When the form is submitted, there are actually two separate bits of dynamic behavior that the application should carry out. One is the actual search, which in the example code we’ll be looking at shortly is delegated to a business tier service. The other is deciding which page to navigate to based on the outcome of the search. That is, if a matching account is found, we want to navigate to a page that displays the results. Otherwise, we want to rerender the Search page with an error message indicating that no matching account was found. To make implementing these types of scenarios as easy as possible, the UICommand component provides an action attribute that can be bound to an arbitrary method on a backing bean via a method binding expression (a special type of value binding expression), as follows: <f:view> <h:form id=”searchForm”> <h:command_button id=”searchButton” value=”Search” action=”#{searchPage.search}”/> </h:form> </f:view> In this example, the button’s action is bound to the search() method of a bean located in either the request or the session under the key searchPage The previous example would result in the following HTML: <form id=”searchForm” method=”post” action=”/ch7ex1/faces/Search.jsp”> <input type=”submit” name=”searchForm:searchButton” value=”Search” title=”Search by account number”/> <input type=”hidden” name=”searchForm” value=”searchForm” /> </form> Note that the UICommand component’s Renderer adds a hidden form attribute to identify which form has been submitted, in case there are multiple forms on the page. The Renderer also prefixes the button’s client ID with the form name (searchForm:searchButton) to ensure that the resulting string it is unique within the page. 226 Chapter 7 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 226 If we wanted to use a hyperlink instead of a button to submit the form, we could simply replace the commandButton tag in the previous example with a commandLink tag as in the following code: <h:commandLink id=”link” action=”#{searchPage.search}”> <f:verbatim>Search</f:verbatim> </h:commandLink> In this case, the component’s Renderer would add a bit of JavaScript to the resulting HTML to enable the hyperlink to submit the form: <form id=”form” method=”post” action=”/ch7ex1/faces/Search.jsp”> <a href=”#” onmousedown=”document.forms[0][form:link’].value=form:link’; document.forms[0].submit()”> Search </a> <input type=”hidden” name=”form:link”/> <input type=”hidden” name=”form” value=”form” /> </form> Note that the Renderer also adds another hidden form field, this time to identify the control that submitted the form, since the HTML anchor tag doesn’t provide an id attribute. Implementing Application Actions Methods that are bound to a UICommand’s action attribute via a method reference expression are referred to as application actions. JSF uses reflection at run time to locate and execute application actions, provided that they observe the following API convention: they must be public methods that take no para- meters and return String. For example, here’s the signature that would be required for the search() method: public String search() When a button or hyperlink backed by a UICommand component is clicked, JSF will look for a method whose signature conforms to this guideline and attempt to invoke it dynamically. To create a working example of a search() method, we would need to code a JavaBean exposing an application action method whose name corresponds to the final portion of the method binding expression. For example, let’s take a look at the search() method in the SearchPage class in Listing 7.1. Navigation, Actions, and Listeners 227 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 227 228 Chapter 7 package com.wiley.masteringjsf.ch7ex1; /** * Page bean used to find the invoices for a given account */ public class SearchPage implements Serializable { public SearchPage() { } /** * Searches for an AccountBean instance corresponding to the value * of the <code>accountNumber</code> attribute. If no match is * found, adds an error message to the FacesContext and returns * <code>null</code>. Otherwise, returns “viewInvoices” after * locating the ViewInvoicesPage and passing it the AccountBean’s * list of invoices. */ public String search() { AccountBean account = null; try { account = delegate.findAccount(accountNumber); } catch (BusinessDelegate.NotFoundException e) { FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, MessageFactory. getMessage(“noMatch”, new Object[] { accountNumber })); return null; } // Do some setup for the next page . . . return “viewInvoices”; } } Listing 7.1 SearchPage.java. 12_462071 Ch07.qxd 5/17/04 10:20 AM Page 228 [...]... perform the clone operation 247 248 Chapter 7 package com.wiley.masteringjsf.ch7ex1; import java.io.Serializable; import java.util.*; import import import import import javax.faces.component.UIData; javax.faces.component.UIViewRoot; javax.faces.context.FacesContext; javax.faces.el.ValueBinding; javax.faces.event.ActionEvent; import com.sun.faces.util.Util; /** * Presents a list of invoices and provides... method, as shown in Listing 7 .6, which delegates the actual search to the findAccount() method of a helper class named BusinessDelegate package com.wiley.masteringjsf.ch7ex1; import java.io.InvalidObjectException; import java.io.Serializable; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import com.sun.faces.util.MessageFactory; import com.sun.faces.util.Util; /** * Page... request or session, as in the following example: public String modifyInvoice() { FacesContext facesContext = FacesContext.getCurrentInstance(); UIViewRoot root = facesContext.getViewRoot(); UIData table = (UIData) root.findComponent(“invoiceForm”).findComponent(“table”); HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest(); request.setAttribute(Constants.CURRENT_INVOICE_BEAN,... “com.wiley.masteringjsf.CURRENT_INVOICE_BEAN”; An alternate approach (and the one used in the example in Listing 7.9) is to make the InvoiceBean a property of ModifyInvoicePage, and set it directly by calling its mutator from within the modifyInvoice() method, as in the following example: public String modifyInvoice() { FacesContext facesContext = FacesContext.getCurrentInstance(); UIViewRoot root = facesContext.getViewRoot();... Listing 7.7 ViewInvoices.jsp Navigation, Actions, and Listeners ... Listing 7.7 (continued) 237 238 Chapter 7 Amount ... “modifyInvoice”; } The ModifyInvoicePage could then return the instance from its getInvoice() method: Navigation, Actions, and Listeners public InvoiceBean getInvoice() { FacesContext facesContext = FacesContext.getCurrentInstance(); Map requestMap = facesContext.getExternalContext().getRequestMap(); return (InvoiceBean) requestMap.get(Constants.CURRENT_INVOICE_BEAN); } When using this approach, make sure that... under a well-known key and let the * detail page find it * * @return the navigation outcome */ public String modifyInvoice() { FacesContext facesContext = FacesContext.getCurrentInstance(); Listing 7.9 ViewInvoicesPage.java Navigation, Actions, and Listeners UIViewRoot root = facesContext.getViewRoot(); UIData table = (UIData) root.findComponent(“invoiceForm”).findComponent(“table”); InvoiceBean invoice... adds an error message to the FacesContext and returns * null Otherwise, returns “viewInvoices” after * locating the ViewInvoicesPage and passing it the AccountBean’s * list of invoices */ public String search() { AccountBean account = null; try { account = delegate.findAccount(accountNumber); } catch (BusinessDelegate.NotFoundException e) { FacesContext context = FacesContext.getCurrentInstance();... Date Discount . com.wiley.masteringjsf.ch7ex1; import java.io.InvalidObjectException; import java.io.Serializable; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import com.sun.faces.util.MessageFactory; import. Figure 6. 8 shows how the Modify Invoice page would look with the added fields: Figure 6. 8 Modify Invoice page with a drop-down menu, radio buttons, and check boxes. UI Components 221 11_ 462 071 Ch 06. qxd. invoices” value=”Delete” action=”#{viewInvoicesPage.delete}”> </h:commandButton> </f:facet> </h:column> Listing 7.7 ViewInvoices.jsp. 12_ 462 071 Ch07.qxd 5/17/04 10:20 AM Page 2 36 <h:column> <f:facet name=”header”> <h:commandLink id=”invoiceNumber” styleClass=”SortLink” title=”Sort