Implementing Custom Components with Classes

   

Implementing Custom Components with Classes

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 in 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

graphics/09fig02.jpg


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 365.

Figure 9-3. Handling Conversion Failures

graphics/09fig03.jpg


Here's how you use corejsf:spinner:

 

 <%@ taglib uri="http://corejsf.com/spinner" prefix="corejsf" %> ... <corejsf:spinner value="#{cardExpirationDate.month}"    id="monthSpinner" minimum="1" maximum="12" size="3"/> <h:message for="monthSpinner"/> ... <corejsf:spinner value="#{cardExpirationDate.year}"    id="yearSpinner" 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 binding, 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.UIComponentTag 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 doesn't 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 368 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 380, we show you how to implement a separate renderer for the spinner.

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

Figure 9-4. JSF Component Hierarchy (not all classes are shown)

graphics/09fig04.jpg


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

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.UIComponentTag

  • 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 bindings. This is another general-purpose map that you can use to store arbitrary value bindings. For example, if the spinner tag has an attribute value="#{cardExpirationDate.month}", then the tag handler constructs a ValueBinding object for the given value binding expression and stores it 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 and 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

graphics/exclamatory_icon.gif

You often need to cast a generic UIComponent parameter to a subclass in order 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.


UIComponentTag is a superclass for the tags you implement for your custom components. It implements mundane things like support for binding, id, and rendered attributes so you can concentrate on supporting the attributes your tags require.

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, validators, value bindings, and method bindings. In this chapter, we're mostly interested in using the Application class to create converters, value bindings, and method bindings.

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 JavaServer Faces
Core JavaServer Faces
ISBN: 0131463055
EAN: 2147483647
Year: 2003
Pages: 121

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