![]() |
ICEfaces Tutorial: Creating Direct-to-DOM Renderers for Custom Components
This tutorial guides you through creating a Direct-to-DOM (D2D) custom renderer using ICEfaces. For more information on Direct-to-DOM rendering, refer to Chapter 3, Key Concepts, .
Note: This is an advanced topic and we recommend that you only read this tutorial if you are building custom components or porting existing component renderers.
The files used in this tutorial are organized in the directory structure shown in Figure 14. Before starting this tutorial, we recommend that you ensure that ICEfaces is installed properly and that your environment is configured to deploy and execute ICEfaces applications on your J2EE application server. Refer to Chapter 2, Configuring Your Environment for ICEfaces, , in the ICEfaces Getting Started Guide.
This tutorial guides you through the process of creating a Direct-to-DOM renderer for a standard component. The source code for this tutorial is included in the basicInputText sample tutorial application.
The renderer is responsible for processing the form data for the component. This is the decoding of the component which is done in the decode method of the renderer. The renderer is also responsible for translating a component into a W3C DOM Element. This is the encoding of the component which is done in the encode methods of the renderer. With a standard component, such as a UIInput with no children, you need only implement the encodeEnd method. The encodeBegin and encodeChildren methods do not need to be implemented for this renderer.
The first method to look at is decode(FacesContext, UIComponent). This method is responsible for taking any parameters that were passed in from a form post and setting the value on the component. The first thing the method does is to validate the context and component parameters, checking to ensure that they are not null. Next, the method ignores all but UIInput components. If the component is a UIInput, then any new value is extracted from the request and put on the component as the submittedValue.
public void decode(FacesContext facesContext, UIComponent uiComponent) { validateParameters(facesContext, uiComponent, null); // only need to decode input components if (!(uiComponent instanceof UIInput)) { return; } // only need to decode enabled, writable components if (isStatic(uiComponent)) { return; } // extract component value from the request map String clientId = uiComponent.getClientId(facesContext); if (clientId == null) { System.out.println("Client id is not defined for decoding"); } Map requestMap = facesContext.getExternalContext().getRequestParameterMap(); if (requestMap.containsKey(clientId)) { String decodedValue = (String) requestMap.get(clientId); // setSubmittedValue is a method in the superclass DomBasicInputRenderer setSubmittedValue(uiComponent, decodedValue); } } // end decodeThe next method to analyze is the encodeEnd(FacesContext, UIComponent). This method is responsible for building the DOM Element to represent the component in the browser. The encodeEnd method uses the DOM methods exposed in the ICEfaces DOMContext and the W3C DOM API. The DOMContext.attachDOMContext method is used to provide a DOMContext to the encodeEnd method. The DOMContext will be initialized if required. As part of the initialization, a root node is created using the DOMContext createRootElement method. The DOMContext API will be used to create new Elements and TextNodes. To set Element attributes and append child nodes to Elements, the W3C DOM API will be used.
public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException { DOMContext domContext = DOMContext.attachDOMContext(facesContext, uiComponent); if (!domContext.isInitialized()) { Element root = domContext.createRootElement("input"); setRootElementId(facesContext, root, uiComponent); root.setAttribute("type", "text"); root.setAttribute("name", uiComponent.getClientId(facesContext)); } Element root = (Element) domContext.getRootNode() ; root.setAttribute("onkeydown",this.ICESUBMIT) ; // create a new String array to hold attributes we will exclude // for the PassThruAttributeRenderer String[] excludesArray = new String[1] ; excludesArray[0] = "onkeydown" ; // the renderAttributes method will not overwrite any attributes // contained in the excludesArray PassThruAttributeRenderer.renderAttributes(facesContext, uiComponent, excludesArray); } // end encodeEndThe following is the content for the faces-config.xml file used in this tutorial. The faces config is used to configure the Renderer for the UIInput Text Component as well as any managed-beans that are required.
<?xml version="1.0"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd" > <faces-config> <render-kit> description>Tutorial Basic Renderer</description> <renderer> <component-family>javax.faces.Input</component-family> <renderer-type>javax.faces.Text</renderer-type> <renderer-class> com.icesoft.tutorial.TutorialInputTextRenderer </renderer-class> </renderer> /render-kit> <managed-bean> <managed-bean-name>tutorial</managed-bean-name> <managed-bean-class>com.icesoft.tutorial.TutorialBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>Iterative renderers are renderers that iterate over a collection of data and render markup for each data point.
A common example of an iterative renderer is the TableRenderer. It renders a UIData component which can be bound to a model bean that provides the collection of data. The renderer iterates over the collection of data and renders a table row for each element in the collection.
There are a few principles to keep in mind when authoring an iterative renderer.
An iterative renderer will render its own children. It is possible, but more complicated to do otherwise; so it is recommended that you override the method public boolean getRendersChildren() from the abstract class javax.faces.render.Renderer. The overriding method in your iterative renderer should return true:
public boolean getRendersChildren() { return true; }In order to properly render the IDs of the DOM Elements, the iterative renderer must maintain the current index of the data set that is being rendered. This state (the current index) exists in the UIComponent class but will be maintained by the renderer since it knows when rendering of the current data point has completed and it is time to move on to the next data point. Each time the renderer finishes rendering a data point, it increments the data index in the UIComponent class. The index is accessed by the renderer class in order to formulate the IDs of the DOM Elements that it is rendering.
|
Copyright 2005-2006. ICEsoft Technologies, Inc. http://www.icesoft.com |