Decoding: Processing Request Values

Encoding: Generating Markup

JSF components generate markup for their user interfaces. By default, the standard JSF components generate HTML. Components can do their own encoding, or they can delegate encoding to a separate renderer. The latter is the more elegant approach because it lets you plug in different renderers for example to encode markup in something other than HTML. However, for simplicity, we will start out with a spinner that renders itself.

Components encode markup with three methods:

  • encodeBegin()

  • encodeChildren()

  • encodeEnd()

The methods are called by JSF at the end of the life cycle, in the order in which they are listed above. JSF invokes encodeChildren only if a component returns true from its getRendersChildren method. By default, getRendersChildren returns false for most components.

For simple components, like our spinner, that do not have children, you do not need to implement encodeChildren. Since we do not need to worry what gets encoded before or after the children, we do all our encoding in encodeBegin.

The spinner generates HTML for a text field and two buttons; that HTML looks like this:

   <input type="text" name="..." value="current value"/>    <input type="submit" name="..." value="<"/>    <input type="submit" name="..." value=">"/>

Here is how that HTML is encoded in UISpinner:

  public class UISpinner extends UIInput {      private static final String MORE = ".more";      private static final String LESS = ".less";      ...      public void encodeBegin(FacesContext context) throws IOException {         ResponseWriter writer = context.getResponseWriter();         String clientId = getClientId(context);         encodeInputField(writer, clientId);         encodeDecrementButton(writer, clientId);         encodeIncrementButton(writer, clientId);      }      private void encodeInputField(ResponseWriter writer, String clientId)            throws IOException {         writer.startElement("input", this);         writer.writeAttribute("name", clientId, "clientId");         Object v = getValue();         if (v != null)            writer.writeAttribute("value", v.toString(), "value");         Integer size = (Integer) getAttributes().get("size");         if (size != null) writer.writeAttribute("size", size, "size");         writer.endElement("input");      }      private void encodeDecrementButton(ResponseWriter writer, String clientId)            throws IOException {         writer.startElement("input", this);         writer.writeAttribute("type", "submit", null);         writer.writeAttribute("name", clientId + LESS, null);         writer.writeAttribute("value", "<", "value");         writer.endElement("input");      }      private void encodeIncrementButton(ResponseWriter writer, String clientId)            throws IOException {         writer.startElement("input", this);         writer.writeAttribute("type", "submit", null);         writer.writeAttribute("name", clientId + MORE, null);         writer.writeAttribute("value", ">", "value");         writer.endElement("input");      }      ...   }     

The ResponseWriter class has convenience methods for writing markup. The start-Element and endElement methods produce the element delimiters. They keep track of child elements, so you do not have to worry about the distinction between <input .../> and <input ...>...</input>. The writeAttribute method writes an attribute name/value pair with the appropriate escape characters.

The last parameter of the startElement and writeAttribute methods is intended for tool support, but it is currently unused. You are supposed to pass the rendered component object or attribute name, or null if the output does not directly correspond to a component or attribute.

UISpinner.encodeBegin faces two challenges. First, it must get the current state of the spinner. The numerical value is easily obtained with the getValue method that the spinner inherits from UIInput. The size is retrieved from the component's attribute map, using the getAttributes method.

(As you will see in the section "Implementing Custom Component Tags" on page 372, the SpinnerTag class stores the tag's size attribute in the component's value expression map, and the get method of the map returned by getAttributes evaluates the value expression.)

Second, the encoding method needs to come up with names for the HTML elements the spinner encodes. It calls the getClientId method to obtain the client ID of the component, which is composed of the ID of the enclosing form and the ID of this component, such as _id1:monthSpinner.

That identifier is created by the JSF implementation. The increment and decrement button names start with the client ID and end in .more and .less, respectively. Here is a complete example of the HTML generated by the spinner:

  <input type="text" name="_id1:monthSpinner" value="1" size="3"/>   <input type="submit" name="_id1:monthSpinner.less"  value="<"/>   <input type="submit" name="_id1:monthSpinner.more"  value=">"/>

In the next section, we discuss how those names are used by the spinner's decode method.

javax.faces.component.UIComponent

  • void encodeBegin(FacesContext context) throws IOException

    The method called in the Render Response phase of the JSF life cycle, only if the component's renderer type is null. This signifies that the component renders itself.

  • String getClientId(FacesContext context)

    Returns the client ID for this component. The JSF framework creates the client ID from the ID of the enclosing form (or, more generally, the enclosing naming container) and the ID of this component.

  • Map getAttributes()

    Returns a mutable map of component attributes and properties. You use this method to view, add, update, or remove attributes from a component. You can also use this map to view or update properties. The map's get and put methods check whether the key matches a component property. If so, the property getter or setter is called.

    As of JSF 1.2, the map also gets attributes that are defined by value expressions. If get is called with a name that is not a property or attribute but a key in the component's value expression map, then the value of the associated expression is returned.


Note

The spinner is a simple component with no children, so its encoding is rather basic. For a more complicated example, see how the tabbed pane renderer encodes markup. That renderer is shown in Listing 9-17 on page 419.


Note

JSF invokes a component's encodeChildren method if the component returns true from getRendersChildren. Interestingly, it does not matter whether the component actually has children as long as the component's getRenders-Children method returns true, JSF calls encodeChildren even if the component has no children.


javax.faces.context.FacesContext

  • ResponseWriter getResponseWriter()

    Returns a reference to the response writer. You can plug your own response writer into JSF if you want. By default, JSF uses a response writer that can write HTML tags.


javax.faces.context.ResponseWriter

  • void startElement(String elementName, UIComponent component)

    Writes the start tag for the specified element. The component parameter lets tools associate a component and its markup. The 1.0 version of the JSF reference implementation ignores this attribute.

  • void endElement(String elementName)

    Writes the end tag for the specified element.

  • void writeAttribute(String attributeName, String attributeValue, String componentProperty)

    Writes an attribute and its value. This method can only be called between calls to startElement() and endElement(). The componentProperty is the name of the component property that corresponds to the attribute. Its use is meant for tools. It is not supported by the 1.0 reference implementation.




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