Download FAQ History |  | API Search Feedback

> ## Creating Custom Component Classes

> As explained in When to Use a Custom Component, a component class defines the state and behavior of a UI component. Some of the state information includes the component's type, identifier, and local value. Some of the behavior defined by the component class includes:

> - Decoding (converting the request parameter to the component's local value)
> - Encoding (converting the local value into the corresponding markup)
> - Updating the model object value with the local value
> - Processing validation on the local value
> - Queueing events

> The `UIComponentBase` class defines the default behavior of a component class. All of the classes representing the standard components extend from `UIComponentBase`. These classes add their own behavior definitions, as your custom component class will do.

> Your custom component class needs to either extend `UIComponentBase` directly or extend a class representing one of the standard components. These classes are located in the `javax.faces.component` package and their names begin with UI.

> To decide whether you need to extend directly from `UIComponentBase` or from one of the standard component classes, consider what behavior you want your component to have. If one of the standard component classes defines most of the functionality you need, you should extend that class rather than `UIComponentBase`. For example, suppose you want to create an editable menu component. It makes sense to have this component extend `UISelectOne` rather than `UIComponentBase` because you can reuse the behavior already defined in `UISelectOne`. The only new functionality you need to define is that which makes the menu editable.

> The image map example has two component classes: `UIArea` and `UIMap`. The `UIMap` component class extends the standard component, `UICommand`. The `UIArea` class extends the standard component, `UIOutput`.

> This following sections explain how to extend a standard component and how to implement the behavior for a component.

> ### Extending From a Standard Component

> Both `UIMap` and `UIArea` extend from standard components. The `UIMap` class represents the component corresponding to the `map` tag:

>     <d:map id="worldMap" currentArea="NAmericas" />

> The `UIArea` class represents the component corresponding to the `area` tag:

>       <d:area id="NAmericas" valueRef="NA"
>         onmouseover="/world_namer.jpg" onmouseout="/world.jpg" />

> The `UIMap` component has one or more `UIArea` components as children. Its behavior consists of:

> - Retrieving the value of the currently-selected area.
> - Rendering the `map` tag and the `input` tag
> - Generating an event when the user clicks on the image map
> - Queuing the event on the `FacesContext`

> The `UIMap` class extends from `UICommand` because `UIMap` generates an `ActionEvent` when a user clicks on the map. Since `UICommand` components already have the ability to generate this kind of event, it makes sense to extend `UICommand` rather than redefining this functionality in a custom component extending from `UIComponentBase`.

> The `UIArea` component class extends `UIOutput` because `UIArea` requires a `value` and `valueRef` attribute, which are already defined by `UIOutput`.

> The `UIArea` component is bound to a model object that stores the shape and coordinates of the region of the image map. You'll see how all of this data is accessed through the `valueRef` expression in Performing Encoding. The behavior of the `UIArea` component consists of:

> - Retrieving the shape and coordinate data from the model object
> - Setting the value of the `selectedArea` tag to the `id` of this component
> - Rendering the `area` tag, including the JavaScript for the `onmouseover`, `onmouseout`, and `onclick` functions

> Although these tasks are actually performed by `AreaRenderer`, the `UIArea` component class must delegate the tasks to `AreaRenderer`. See Delegating Rendering to a Renderer for more information.

> The rest of these components' behavior is performed in its encoding and decoding methods. Performing Encoding and Performing Decoding explain how this behavior is implemented.

> ### Performing Encoding

> During the Render Response phase, the JavaServer Faces implementation processes the encoding methods of all components and their associated renderers in the tree. The encoding methods convert the current local value of the component into the corresponding markup that represents it in the response.

> The `UIComponentBase` class defines a set of methods for rendering markup: `encodeBegin`, `encodeChildren`, `encodeEnd`. If the component has child components, you might need to use more than one of these methods to render the component; otherwise, all rendering should be done in `encodeEnd`.

> Since `UIMap` is a parent component of `UIArea`, the `area` tags must be rendered after the beginning `map` tag and before the ending `map` tag. To accomplish this, the `UIMap` class renders the beginning `map` tag in `encodeBegin` and the rest of the `map` tag in `encodeEnd`.

> The JavaServer Faces implementation will automatically invoke the `encodeEnd` method of the `UIArea` component's renderer after it invokes `UIMap`'s `encodeBegin` method and before it invokes `UIMap`'s `encodeEnd` method. If a component needs to perform the rendering for its children, it does this in the `encodeChildren` method.

> Here are the `encodeBegin` and `encodeEnd` methods of `UIMap`:

>     public void encodeBegin(FacesContext context) throws
>     IOException {
>       if (context == null) {
>         System.out.println("Map: context is null");
>         throw new NullPointerException();
>       }
>       ResponseWriter writer = context.getResponseWriter();
>       writer.write("<Map name=\"");
>       writer.write(getComponentId());
>       writer.write("\">");
>     }
>     public void encodeEnd(FacesContext context) throws IOException
>     {
>       if (context == null) {
>         throw new NullPointerException();
>       }
>       ResponseWriter writer = context.getResponseWriter();
>       writer.write(
>         "<input type=\"hidden\" name=\"selectedArea\"");
>       writer.write("\">");
>       writer.write("</Map>");
>     }

> Notice that `encodeBegin` renders only the beginning `map` tag. The `encodeEnd` method renders the `input` tag and the ending `map` tag.

> These methods first check if the `FacesContext` is null. The `FacesContext` contains all of the information associated with the current request.

> You also need a `ResponseWriter`, which you get from the `FacesContext`. The `ResponseWriter` writes out the markup to the current response.

> The rest of the method renders the markup to the `ResponseWriter`. This basically involves passing the HTML tags and attributes to the `ResponseWriter` as strings, retrieving the values of the component attributes, and passing these values to the `ResponseWriter`.

> The `id` attribute value is retrieved with the `getComponentId` method, which returns the component's unique identifier. The other attribute values are retrieved with the `getAttribute` method, which takes the name of the attribute.

> If you want your component to perform its own rendering but delegate to a Renderer if there is one, include the following lines in the encode method to check if there is a renderer associated with this component.

>       if (getRendererType() != null) {
>         super.encodeEnd(context);
>         return;
>       }

> If there is a Renderer available, this method invokes the superclass' `encodeEnd` method, which does the work of finding the renderer. The `UIMap` class performs its own rendering so does not need to check for available renderers.

> In some custom component classes that extend standard components, you might need to implement additional methods besides `encodeEnd`. For example, if you need to retrieve the component's value from the request parameters--such as to update a model object--you also have to implement the `decode` method.

> ### Performing Decoding

> During the Apply Request Values phase, the JavaServer Faces implementation processes the `decode` methods of all components in the tree. The `decode` method extracts a component's local value from incoming request parameters and converts the value to a type acceptable to the component class.

> A custom component class needs to implement the decode method only if it must retrieve the local value, or it needs to queue events onto the `FacesContext`. The `UIMap` component must do both of the tasks. Here is the decode method of `UIMap`:

>     public void decode(FacesContext context) throws IOException {
>       if (context == null) {
>         throw new NullPointerException();
>       }
>       String value =
>         context.getServletRequest().getParameter("selectedArea");
>       if (value != null)
>         setAttribute("currentArea", value);
>         context.addFacesEvent(
>           new ActionEvent(this, commandName));
>         setValid(true);
>     }

> The `decode` method first extracts the value of `selectedArea` from the request parameters. Then, it sets the value of `UIMap`'s `currentArea` attribute to the value of `selectedArea`. The `currentArea` attribute value indicates the currently-selected area.

> The `decode` method queues an action event onto the `FacesContext`. In the JSP page, the `action_listener` tag nested inside the `map` tag causes the `ImageMapEventHandler` to be registered on the `map` component. This event handler will handle the queued event during the Apply Request Values phase, as explained in Handling Events for Custom Components.

> Finally, the `decode` method calls `setValid(true)` to confirm that the local values are valid.

Download FAQ History |  | API Search Feedback

All of the material in The Java(TM) Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.