Encoding: Generating Markup

Chapter 9. Custom Components, Converters, and Validators

Topics in This Chapter

  • "Classes for Implementing Custom Components" on page 356

  • "Encoding: Generating Markup" on page 362

  • "Decoding: Processing Request Values" on page 366

  • "Implementing Custom Component Tags" on page 372

  • "Revisiting the Spinner" on page 387

  • "Encoding JavaScript to Avoid Server Roundtrips" on page 404

  • "Using Child Components and Facets" on page 408

  • "Implementing Custom Converters and Validators"on page 432

JSF provides a basic set of components for building HTML-based web applications, such as text fields, checkboxes, buttons, and so on. However, most user interface designers want more advanced components, such as calendars, tabbed panes, or navigation trees, that are not part of the standard JSF component set. Fortunately, JSF makes it possible to build reusable JSF components with rich behavior.

This chapter shows you how to implement custom components. We use two custom components a spinner and a tabbed pane, shown in Figure 9-1 to illustrate the various aspects of creating custom components.

Figure 9-1. The spinner and the tabbed pane


The JSF API lets you implement custom components and associated tags with the same features as the JSF standard tags. For example, h:input uses a value expression to associate a text field's value with a bean property, so you could use value expressions to wire calendar cells to bean properties. JSF standard input components fire value change events when their value changes, so you could fire value change events when a different date is selected in the calendar.

The first part of this chapter uses the spinner component to illustrate basic issues that you encounter in all custom components. We then revisit the spinner to show more advanced issues:

  • "Using an External Renderer" on page 387

  • "Calling Converters from External Renderers" on page 393

  • "Supporting Value Change Listeners" on page 394

  • "Supporting Method Expressions" on page 396

The second half of the chapter examines a tabbed pane component that illustrates the following aspects of custom component development.

  • "Processing SelectItem Children" page 411

  • "Processing Facets" on page 412

  • "Encoding CSS Styles" on page 413

  • "Using Hidden Fields" on page 415

  • "Saving and Restoring State" on page 415

  • "Firing Action Events" on page 418

Classes for Implementing Custom Components

In the following sections, we discuss the classes that you need to implement custom components.

To motivate the discussion, we will develop a spinner component. A spinner lets you enter a number in a text field, either by typing it directly into the field or by activating an increment or decrement button. Figure 9-2 shows an application that uses two spinners for a credit card's expiration date, one for the month and another for the year.

Figure 9-2. Using the spinner component


In Figure 9-2, from top to bottom, all proceeds as expected. The user enters valid values, so navigation takes us to a designated JSF page that echoes those values.

The spinner insists on integer values. Figure 9-3 shows an attempt to enter bad data. We let the standard integer converter handle conversion errors. You can see how we did it in "Using Converters" on page 369.

Figure 9-3. Handling conversion failures


Here is how you use corejsf:spinner:

  <%@ taglib uri="http://corejsf.com/spinner" prefix="corejsf" %>   ...   <corejsf:spinner value="#{cardExpirationDate.month}"       minimum="1" maximum="12" size="3"/>   <h:message for="monthSpinner"/>   ...   <corejsf:spinner value="#{cardExpirationDate.year}"       minimum="1900" maximum="2100" size="5"/>   <h:message for="yearSpinner"/>

The corejsf:spinner tag supports the following attributes:

  • binding

  • id

  • minimum

  • maximum

  • rendered

  • size

  • value

Only one of the attributes value is required.

The minimum and maximum attributes let you assign a range of valid values for example, the month spinner has a minimum of 1 and a maximum of 12. You can also limit the size of the spinner's text field with the size attribute. The value attribute can take a literal string for example, value="2"; or a value expression for example, value="#{someBean.someProperty}".

Finally, the spinner supports the binding, id, and rendered attributes, which are discussed in Chapter 4. Support for those attributes is free because our tag class extends the javax.faces.webapp.UIComponentELTag class.

In the preceding code fragment we assigned explicit identifiers to our spinners with the id attribute. We did that so we could display conversion errors with h:message. The spinner component does not require users to specify an identifier. If an identifier is not specified, JSF generates one automatically.

Users of JSF custom tags need not understand how those tags are implemented. Users simply need to know the functionality of a tag and the set of available attributes. Just as for any component model, the expectation is that a few skilled programmers will create tags that can be used by many page developers.

Tags and Components

Minimally, a tag for a JSF custom component requires two classes:

  • A class that processes tag attributes. By convention, the class name has a Tag suffix for example, SpinnerTag.

  • A component class that maintains state, renders a user interface, and processes input. By convention, the class name has a UI prefix for example, UISpinner.

The tag class is part of the plumbing. It creates the component and transfers tag attribute values to component properties and attributes. The implementation of the tag class is largely mechanical. See "Implementing Custom Component Tags" on page 372 for more information on tag classes.

The UI class does the important work. It has two separate responsibilities:

  • To render the user interface by encoding markup

  • To process user input by decoding the current HTTP request

Component classes can delegate rendering and processing input to a separate renderer. By using different renderers, you can support multiple clients, such as web browsers and cell phones. Initially, our spinner component will render itself, but in "Using an External Renderer" on page 387, we show you how to implement a separate renderer for the spinner.

A component's UI class must extend the UIComponent class. That class defines over 40 abstract methods, so you will want to extend an existing class that implements them. You can choose from the classes shown in Figure 9-4.

Figure 9-4. JSF component hierarchy (not all classes are shown)


Our UISpinner class will extend UIInput, which extends UIOutput and implements the EditableValueHolder interface. Our UITabbedPane will extend UICommand, which implements the ActionSource2 interface.

Note

The ActionSource2 interface was added in JSF1.2. An ActionSource has methods to manage action listeners. An ActionSource2 additionally manages actions. In JSF 1.1, the ActionSource interface handled both actions and action listeners.


The Custom Component Developer's Toolbox

When you implement custom components, you will become very familiar with a handful of JSF classes:

  • javax.faces.component.UIComponent

  • javax.faces.webapp.UIComponentELTag

  • javax.faces.context.FacesContext

  • javax.faces.application.Application

  • javax.faces.context.ResponseWriter

UIComponent is an abstract class that defines what it means to be a component. Each component manages several important categories of data. These include:

  • A list of child components. For example, the children of the h:panelGrid component are the components that are placed in the grid location. However, a component need not have any children.

  • A map of facet components. Facets are similar to child components, but each facet has a key, not a position in a list. It is up to the component how to lay out its facets. For example, the h:dataTable component has header and footer facets.

  • A map of attributes. This is a general-purpose map that you can use to store arbitrary key/value pairs.

  • A map of value expressions. This is another general-purpose map that you can use to store arbitrary value expressions. For example, if a spinner tag has an attribute value="#{cardExpirationDate.month}", then the component stores a ValueExpression object for the given value expression under the key "value".

  • A collection of listeners. This collection is maintained by the JSF framework.

When you define your own JSF components, you usually subclass one of the following three standard component classes:

  • UICommand, if your component produces actions similar to a command button or link

  • UIOutput, if your component displays a value but does not allow the user to edit it

  • UIInput, if your component reads a value from the user (such as the spinner)

If you look at Figure 9-4, you will find that these three classes implement interfaces that specify these distinct responsibilities:

  • ActionSource defines methods for managing action listeners.

  • ActionSource2 defines methods for managing actions.

  • ValueHolder defines methods for managing a component value, a local value, and a converter.

  • EditableValueHolder extends ValueHolder and adds methods for managing validators and value change listeners.

Tip

You often need to cast a generic UIComponent parameter to a subclass to access values, converters, and so on. Rather than casting to a specific class such as UISpinner, cast to an interface type, such as ValueHolder. That makes it easier to reuse your code.


The FacesContext class contains JSF-related request information. Among other things, you can access request parameters through FacesContext, get a reference to the Application object, get the current view root component, or get a reference to the response writer, which you use to encode markup.

The Application class keeps track of objects shared by a single application for example, the set of supported locales, and available converters and validators. The Application class also serves as a factory, with factory methods for components, converters, and validators. In this chapter, we are mostly interested in using the Application class to create converters, and to obtain an expression factory for value and method expressions.

Nearly all custom components generate markup, so you will want to use the ResponseWriter class to ease that task. Response writers have methods for starting and ending HTML elements, and methods for writing element attributes.

We now return to the spinner implementation and view the spinner from a number of different perspectives. We start with every component's most basic tasks generating markup and processing requests and then turn to the more mundane issue of implementing the corresponding tag handler class.



Core JavaServerT Faces
Core JavaServer(TM) Faces (2nd Edition)
ISBN: 0131738860
EAN: 2147483647
Year: 2004
Pages: 84

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net