Implementing Custom Component Tags

   

Now that you have seen how to implement the spinner component, there is one remaining chore: to supply a tag handler. Component tag handlers are similar to the tag handlers for converters and validators that you saw in Chapter 6. A tag handler needs to gather the attributes that were supplied in the JSF tag and move them into the component object.

Follow these steps to create a tag handler for your custom component:

  • Implement a tag class.

  • Create (or update) a tag library descriptor (TLD).

JSF provides two tag superclasses, UIComponentTag and UIComponentBodyTag, that you can extend to implement your tag class. You extend the former if your component does not process its body (that is, the child tags and text between the start and end tag), and the latter if it does. Only four of the standard JSF tags extend UIComponentBodyTag: f:view, f:verbatim, h:commandLink, and h:outputLink. Our spinner component does not process its body, so it extends UIComponentTag.

NOTE

graphics/note_icon.gif

A tag that implements UIComponentTag can have a body, provided that the body tags know how to process themselves. For example, you can add an f:attribute child to a spinner.


Let's look at the implementation of the SpinnerTag class:

 

 public class SpinnerTag extends UIComponentTag {    private String minimum = null;    private String maximum = null;    private String size = null;    private String value = null;    ... } 

The spinner tag class has an instance field for each attribute. The tag class should keep all attributes as String objects, so that the tag user can supply either value binding expressions or values.

Tag classes have five responsibilities:

  • To identify a component type

  • To identify a renderer type

  • To provide setter methods for tag attributes

  • To store tag attribute values in the tag's component

  • To release resources

The SpinnerTag class identifies its component type as com.corejsf.Spinner and its renderer type as null. A null renderer type means that a component renders itself or nominates its own renderer.

 

 public String getComponentType() { return "com.corejsf.Spinner"; } public String getRendererType()  { return null; } 

SpinnerTag provides setter methods for the attributes it supports: minimum, maximum, value, and size.

 

 public void setMinimum(String newValue) { minimum = newValue; } public void setMaximum(String newValue) { maximum = newValue; } public void setSize(String newValue) { size = newValue; } public void setValue(String newValue) { value = newValue; } 

Tags must override a setProperties method to copy tag attribute values to the component. The method name is somewhat of a misnomer because it usually sets component attributes or value bindings, not properties.

 

 public void setProperties(UIComponent component) {    // always call the superclass method    super.setProperties(component);    setInteger(component, "size", size);    setInteger(component, "minimum", minimum);    setInteger(component, "maximum", maximum);    setString(component, "value", value); } 

The spinner tag's setInteger and setString methods are helper methods that set a component attribute or value binding. Here is the setInteger method:

 

 public void setInteger(UIComponent component,       String attributeName, String attributeValue) {    if (attributeValue == null) return;    if (isValueReference(attributeValue))       setValueBinding(component, attributeName, attributeValue);    else       component.getAttributes().put(attributeName,             new Integer(attributeValue)); } 

If the attribute value is a value reference (such as "#{cardExpirationDate.year}), then we call the setValueBinding helper method. That method goes through the usual laborious contortions to create a ValueBinding object and to pass it to the component's map of value bindings.

 

 public void setValueBinding(UIComponent component,       String attributeName, String attributeValue) {    FacesContext context = FacesContext.getCurrentInstance();    Application app = context.getApplication();    ValueBinding vb = app.createValueBinding(attributeValue);    component.setValueBinding(attributeName, vb); } 

If the attribute value is not a value reference, we convert the attribute value to the target type and put it into the component's attribute map.

NOTE

graphics/note_icon.gif

The map returned by the UIComponent.getAttributes method is smart: it can access both attributes and properties. For example, if you call the map's put method with an attribute whose name is "value", the setValue method is called. If the attribute name is "minimum", the name/value pair is put into the component's attribute map since the UISpinner class doesn't have a setMinimum method. Unfortunately, the map isn't smart enough to deal with value bindings.


Finally, tags must implement the release method to release resources and reset all instance fields, so that the tag object can be reused for parsing other tags.

 

 public void release() {    // always call the superclass method    super.release();    minimum = null;    maximum = null;    size = null;    value = null; } 

NOTE

graphics/note_icon.gif

Tag classes must call superclass methods when they override setProperties and release.


Listing 9-2 contains the complete code for the tag handler.

After you've created your tag class, you need to declare your new tag in a tag library descriptor. Listing 9-3 shows how the corejsf:spinner tag is defined. You might notice that we've declared three attributes in the TLD that are not in the SpinnerTag class: binding, id, and rendered. We don't need accessor methods for them in the SpinnerTag class, because those methods are implemented by UIComponentTag.

Listing 9-2. spinner/WEB-INF/classes/com/corejsf/SpinnerTag.java
  1. package com.corejsf;  2.  3. import javax.faces.application.Application;  4. import javax.faces.component.UIComponent;  5. import javax.faces.context.FacesContext;  6. import javax.faces.el.ValueBinding;  7. import javax.faces.webapp.UIComponentTag;  8.  9. public class SpinnerTag extends UIComponentTag { 10.   private String minimum = null; 11.   private String maximum = null; 12.   private String size = null; 13.   private String value = null; 14. 15.   public String getRendererType() { return null; } 16.   public String getComponentType() { return "com.corejsf.Spinner"; } 17. 18.   public void setMinimum(String newValue) { minimum = newValue; } 19.   public void setMaximum(String newValue) { maximum = newValue; } 20.   public void setSize(String newValue) { size = newValue; } 21.   public void setValue(String newValue) { value = newValue; } 22. 23.   public void setProperties(UIComponent component) { 24.      // always call the superclass method 25.      super.setProperties(component); 26. 27.      setInteger(component, "size", size); 28.      setInteger(component, "minimum", minimum); 29.      setInteger(component, "maximum", maximum); 30.      setString(component, "value", value); 31.   } 32. 33.   public void setInteger(UIComponent component, 34.         String attributeName, String attributeValue) { 35.      if (attributeValue == null) return; 36.      if (isValueReference(attributeValue)) 37.         setValueBinding(component, attributeName, attributeValue); 38.      else 39.         component.getAttributes().put(attributeName, 40.               new Integer(attributeValue)); 41.   } 42. 43.   public void setString(UIComponent component, 44.         String attributeName, String attributeValue) { 45.      if (attributeValue == null) return; 46.      if (isValueReference(attributeValue)) 47.         setValueBinding(component, attributeName, attributeValue); 48.      else 49.         component.getAttributes().put(attributeName, attributeValue); 50.   } 51. 52.   public void setValueBinding(UIComponent component, 53.         String attributeName, String attributeValue) { 54.      FacesContext context = FacesContext.getCurrentInstance(); 55.      Application app = context.getApplication(); 56.      ValueBinding vb = app.createValueBinding(attributeValue); 57.  component.setValueBinding(attributeName, vb); 58.   } 59. 60.   public void release() { 61.      // always call the superclass method 62.      super.release(); 63. 64.      minimum = null; 65.      maximum = null; 66.      size = null; 67.      value = null; 68.   } 69. } 

Listing 9-3. spinner/WEB-INF/spinner.tld
  1. <?xml version="1.0" encoding="ISO-8859-1" ?>  2. <!DOCTYPE taglib  3.   PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  4.   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">  5. <taglib>  6.    <tlib-version>0.03</tlib-version>  7.    <jsp-version>1.2</jsp-version>  8.    <short-name>spinner</short-name>  9.    <uri>http://corejsf.com/spinner</uri> 10.   <description>This tag library contains a spinner tag</description> 11. 12.   <tag> 13.      <name>spinner</name> 14.      <tag-class>com.corejsf.SpinnerTag</tag-class> 15. 16.      <attribute> 17.         <name>binding</name> 18.         <description>A value binding that points to a bean property</description> 19.      </attribute> 20. 21.      <attribute> 22.         <name>id</name> 23.         <description>The client id of this component</description> 24.      </attribute> 25. 26.      <attribute> 27.         <name>rendered</name> 28.         <description>Is this component rendered?</description> 29.      </attribute> 30. 31.      <attribute> 32.         <name>minimum</name> 33.         <description>The spinner minimum value</description> 34.      </attribute> 35. 36.      <attribute> 37.         <name>maximum</name> 38.         <description>The spinner maximum value</description> 39.      </attribute> 40. 41.      <attribute> 42.         <name>size</name> 43.         <description>The size of the input field</description> 44.      </attribute> 45. 46.      <attribute> 47.         <name>value</name> 48.         <required>true</required> 49.         <description>The value of the spinner</description> 50.      </attribute> 51.   </tag> 52. </taglib> 

 

graphics/api_icon.gif

 

 javax.faces.webapp.UIComponentTag 

  • void setProperties(UIComponent component)

    Transfers tag attribute values to component properties, attributes, or both. Custom components must call the superclass setProperties method to make sure that properties are set for the attributes UIComponentTag supports: binding, id, and rendered.

 

graphics/api_icon.gif

 

 javax.faces.context.FacesContext 

  • static FacesContext getCurrentInstance()

    Returns a reference to the current FacesContext instance.

 

graphics/api_icon.gif

 

 javax.faces.application.Application 

  • ValueBinding createValueBinding(String valueReferenceExpression)

    Creates a value binding and stores it in the application. The string must be a value reference expression of this form: #{...}

 

graphics/api_icon.gif

 

 javax.faces.component.UIComponent 

  • void setValueBinding(String name, ValueBinding valueBinding)

    Stores a value binding by name in the component.

 

graphics/api_icon.gif

 

 javax.faces.webapp.UIComponentTag 

  • static boolean isValueReference(String expression)

    Returns true if expression starts with "#{" and ends with "}".

The Spinner Application

After a number of different perspectives of the spinner component, it's time to take a look at the spinner example in its entirety. This section lists the code for the spinner test application shown in Figure 9-1 on page 351. The directory structure is shown in Figure 9-5 and the code is shown in Listing 9-4 through Listing 9-9.

Listing 9-4. spinner/index.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  4.    <%@ taglib uri="http://corejsf.com/spinner" prefix="corejsf" %>  5.    <f:view>  6.       <head>  7.          <link href="styles.css" rel="stylesheet" type="text/css"/>  8.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  9.          <title><h:outputText value="#{msgs.windowTitle}"/></title> 10.       </head> 11.       <body> 12.          <h:form > 13.             <h:outputText value="#{msgs.creditCardExpirationPrompt}" 14.                style/> 15.             <p/> 16.             <h:panelGrid columns="3"> 17.                <h:outputText value="#{msgs.monthPrompt}"/> 18.                <corejsf:spinner value="#{cardExpirationDate.month}" 19.                    minimum="1" maximum="12" size="3"/> 20.                <h:message for="monthSpinner"/> 21.                <h:outputText value="#{msgs.yearPrompt}"/> 22.                <corejsf:spinner value="#{cardExpirationDate.year}" 23.                    minimum="1900" maximum="2100" size="5"/> 24.                <h:message for="yearSpinner"/> 25.             </h:panelGrid> 26.             <p/> 27.             <h:commandButton value="#{msgs.nextButtonPrompt}" action="next"/> 28.          </h:form> 29.       </body> 30.    </f:view> 31. </html> 

Listing 9-5. spinner/next.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  4.  5.    <f:view>  6.       <head>  7.          <link href="styles.css" rel="stylesheet" type="text/css"/>  8.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  9.          <title><h:outputText value="#{msgs.windowTitle}"/></title> 10.       </head> 11.       <body> 12.          <h:form> 13.             <h:outputText value="#{msgs.youEnteredPrompt}" style/> 14.             <p> 15.             <h:outputText value="#{msgs.expirationDatePrompt}"/> 16.                <h:outputText value="#{cardExpirationDate.month}"/> / 17.                <h:outputText value="#{cardExpirationDate.year}"/> 18.             <p> 19.             <h:commandButton value="Try again" action="again"/> 20.          </h:form> 21.       </body> 22.    </f:view> 23. </html> 

Listing 9-6. spinner/WEB-INF/classes/com/corejsf/CreditCardExpiration.java
  1. package com.corejsf;  2.  3. public class CreditCardExpiration {  4.    private int month = 1;  5.    private int year = 2000;  6.  7.    // PROPERTY: month  8.    public int getMonth() { return month; }  9.    public void setMonth(int newValue) { month = newValue; } 10. 11.    // PROPERTY: year 12.    public int getYear() { return year; } 13.    public void setYear(int newValue) { year = newValue; } 14. } 

Listing 9-7. spinner/WEB-INF/faces-config.xml
  1. <?xml version="1.0"?>  2.  3. <!DOCTYPE faces-config PUBLIC  4.   "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"  5.   "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">  6.  7. <faces-config>  8.  9.    <navigation-rule> 10.       <from-view-id>/index.jsp</from-view-id> 11.       <navigation-case> 12.          <from-outcome>next</from-outcome> 13.          <to-view-id>/next.jsp</to-view-id> 14.       </navigation-case> 15.    </navigation-rule> 16. 17.    <navigation-rule> 18.       <from-view-id>/next.jsp</from-view-id> 19.       <navigation-case> 20.          <from-outcome>again</from-outcome> 21.          <to-view-id>/index.jsp</to-view-id> 22.       </navigation-case> 23.    </navigation-rule> 24. 25.    <component> 26.       <component-type>com.corejsf.Spinner</component-type> 27.       <component-class>com.corejsf.UISpinner</component-class> 28.    </component> 29. 30.    <managed-bean> 31.       <managed-bean-name>cardExpirationDate</managed-bean-name> 32.       <managed-bean-class>com.corejsf.CreditCardExpiration</managed-bean-class> 33.       <managed-bean-scope>session</managed-bean-scope> 34.    </managed-bean> 35. 36. </faces-config> 

Listing 9-8. spinner/WEB-INF/classes/com/corejsf/messages.properties
 1. windowTitle=Spinner Test 2. creditCardExpirationPrompt=Please enter your credit card expiration date: 3. monthPrompt=Month: 4. yearPrompt=Year: 5. nextButtonPrompt=Next 6. youEnteredPrompt=You entered: 7. expirationDatePrompt=Expiration Date 8. changes=Changes: 

Listing 9-9. spinner/styles.css
 1. body { 2.    background: #eee; 3. } 4. .pageTitle { 5.    font-size: 1.25em; 6. } 

Figure 9-5. Spinner Directory Structure

graphics/09fig05.jpg




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