|
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:
The
UIComponentBaseclass defines the default behavior of a component class. All of the classes representing the standard components extend fromUIComponentBase. These classes add their own behavior definitions, as your custom component class will do.Your custom component class needs to either extend
UIComponentBasedirectly or extend a class representing one of the standard components. These classes are located in thejavax.faces.componentpackage and their names begin with UI.To decide whether you need to extend directly from
UIComponentBaseor 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 thanUIComponentBase. For example, suppose you want to create an editable menu component. It makes sense to have this component extendUISelectOnerather thanUIComponentBasebecause you can reuse the behavior already defined inUISelectOne. The only new functionality you need to define is that which makes the menu editable.The image map example has two component classes:
UIAreaandUIMap. TheUIMapcomponent class extends the standard component,UICommand. TheUIAreaclass 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
UIMapandUIAreaextend from standard components. TheUIMapclass represents the component corresponding to themaptag:The
UIAreaclass represents the component corresponding to theareatag:The
UIMapcomponent has one or moreUIAreacomponents as children. Its behavior consists of:The
UIMapclass extends fromUICommandbecauseUIMapgenerates anActionEventwhen a user clicks on the map. SinceUICommandcomponents already have the ability to generate this kind of event, it makes sense to extendUICommandrather than redefining this functionality in a custom component extending fromUIComponentBase.The
UIAreacomponent class extendsUIOutputbecauseUIArearequires avalueandvalueRefattribute, which are already defined byUIOutput.The
UIAreacomponent 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 thevalueRefexpression in Performing Encoding. The behavior of theUIAreacomponent consists of:Although these tasks are actually performed by
AreaRenderer, theUIAreacomponent class must delegate the tasks toAreaRenderer. 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
UIComponentBaseclass 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 inencodeEnd.Since
UIMapis a parent component ofUIArea, theareatags must be rendered after the beginningmaptag and before the endingmaptag. To accomplish this, theUIMapclass renders the beginningmaptag inencodeBeginand the rest of themaptag inencodeEnd.The JavaServer Faces implementation will automatically invoke the
encodeEndmethod of theUIAreacomponent's renderer after it invokesUIMap'sencodeBeginmethod and before it invokesUIMap'sencodeEndmethod. If a component needs to perform the rendering for its children, it does this in theencodeChildrenmethod.Here are the
encodeBeginandencodeEndmethods ofUIMap: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
encodeBeginrenders only the beginningmaptag. TheencodeEndmethod renders theinputtag and the endingmaptag.These methods first check if the
FacesContextis null. TheFacesContextcontains all of the information associated with the current request.You also need a
ResponseWriter, which you get from theFacesContext. TheResponseWriterwrites 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 theResponseWriteras strings, retrieving the values of the component attributes, and passing these values to theResponseWriter.The
idattribute value is retrieved with thegetComponentIdmethod, which returns the component's unique identifier. The other attribute values are retrieved with thegetAttributemethod, 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 there is a Renderer available, this method invokes the superclass'
encodeEndmethod, which does the work of finding the renderer. TheUIMapclass 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 thedecodemethod.Performing Decoding
During the Apply Request Values phase, the JavaServer Faces implementation processes the
decodemethods of all components in the tree. Thedecodemethod 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. TheUIMapcomponent must do both of the tasks. Here is the decode method ofUIMap: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
decodemethod first extracts the value ofselectedAreafrom the request parameters. Then, it sets the value ofUIMap'scurrentAreaattribute to the value ofselectedArea. ThecurrentAreaattribute value indicates the currently-selected area.The
decodemethod queues an action event onto theFacesContext. In the JSP page, theaction_listenertag nested inside themaptag causes theImageMapEventHandlerto be registered on themapcomponent. This event handler will handle the queued event during the Apply Request Values phase, as explained in Handling Events for Custom Components.Finally, the
decodemethod callssetValid(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.