Revisiting the Spinner

   

Let's revisit the spinner listed in the previous section. That spinner has two serious drawbacks. First, the spinner component renders itself, so you couldn't, for example, attach a separate renderer to the spinner when you migrate your application to cell phones. Second, the spinner requires a roundtrip to the server every time a user clicks on the increment or decrement button. Nobody would implement an industrial-strength spinner with those deficiencies. Let's see how to address them.

While we are at it, we will also add another feature to the spinner the ability to attach value change listeners.

Using an External Renderer

In the preceding example, the UISpinner class was in charge of its own rendering. However, most UI classes delegate rendering to a separate class. Using separate renderers is a good idea: it becomes easy to replace renderers, to adapt to a different UI toolkit, or simply to achieve different HTML effects. In "Encoding JavaScript to Avoid Server Roundtrips" on page 400 we see how to use an alternative renderer that uses JavaScript to keep track of the spinner's value on the client.

Using an external renderer requires these steps:

  • Define an ID string for your renderer.

  • Declare the renderer in a JSF configuration file.

  • Modify your tag class to return the renderer's ID from getRendererType().

  • Implement the renderer class.

The identifier in our case, com.corejsf.Spinner must be defined in a JSF configuration file, like this:

 

 <faces-config>    ...    <component>       <component-type>com.corejsf.Spinner</component-type>       <component-class>com.corejsf.UISpinner</component-class>    </component>    <render-kit>       <renderer>          <component-family>javax.faces.Input</component-family>          <renderer-type>com.corejsf.Spinner</renderer-type>          <renderer-class>com.corejsf.SpinnerRenderer</renderer-class>       </renderer>    </render-kit> </faces-config> 

The component-family element serves to overcome a historical problem. The names of the standard HTML tags are meant to indicate the component type and the renderer type. For example, an h:selectOneMenu is a UISelectOne component whose renderer has type javax.faces.Menu. That same renderer can also be used for the h:selectManyMenu tag. But the scheme didn't work so well. The renderer for h:inputText writes an HTML input text field. That renderer won't work for h:outputText you don't want to use a text field for output. So, instead of identifying renderers by individual components, renderers are determined by the renderer type and the component family. Table 9-1 shows the component families of all standard component classes. In our case, we use the component family javax.faces.Input because UISpinner is a subclass of UIInput.

Table 9-1. Component Families of Standard Component Classes

Component Class

Component Family

UICommand

javax.faces.Command

UIData

javax.faces.Data

UIForm

javax.faces.Form

UIGraphic

javax.faces.Graphic

UIInput

javax.faces.Input

UIMessage

javax.faces.Message

UIMessages

javax.faces.Messages

UIOutput

javax.faces.Output

UIPanel

javax.faces.Panel

UISelectBoolean

javax.faces.SelectBoolean

UISelectMany

javax.faces.SelectMany

UISelectOne

javax.faces.SelectOne


The getRendererType of your tag class needs to return the renderer ID.

 

 public class SpinnerTag extends UIComponentTag {    ...    public String getComponentType() { return "com.corejsf.Spinner"; }    public String getRendererType()  { return "com.corejsf.Spinner"; }    ... } 

NOTE

graphics/note_icon.gif

Component IDs and Renderer IDs have separate name spaces. It is okay to use the same string as a component ID and a renderer ID.


It is also a good idea to set the renderer type in the component constructor:

 

 public class UISpinner extends UIInput {    public UISpinner() {       setConverter(new IntegerConverter()); // to convert the submitted value       setRendererType("com.corejsf.Spinner"); // this component has a renderer    } } 

Then the renderer type is properly set if a component is programmatically constructed of components, without the use of tags.

The final step is implementing the renderer itself. Renderers extend the javax.faces.render.Renderer class. That class has seven methods, four of which are familiar:

  • void encodeBegin(FacesContext context, UIComponent component)

  • void encodeChildren(FacesContext context, UIComponent component)

  • void encodeEnd(FacesContext context, UIComponent component)

  • void decode(FacesContext context, UIComponent component)

The renderer methods listed above are almost identical to their component counterparts except that the renderer methods take an additional argument: a reference to the component being rendered. To implement those methods for the spinner renderer, we move the component methods to the renderer and apply code changes to compensate for the fact that the renderer is passed a reference to the component. That's easy to do.

Here are the remaining Renderer methods:

  • Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)

  • boolean getRendersChildren()

  • String convertClientId(FacesContext context, String clientId)

The getConvertedValue method converts a component's submitted value from a string to an object. The default implementation in the Renderer class simply returns the value.

The getRendersChildren method specifies whether a renderer is responsible for rendering its component's children. If that method returns true, JSF will call the renderer's encodeChildren method; if it returns false (the default behavior), the JSF implementation won't call that method.

The convertClientId method converts an ID string (such as _id1:monthSpinner) so that it can be used on the client some clients may place restrictions on IDs, such as disallowing special characters. However, the default implementation simply returns the ID string, unchanged.

If you have a component that renders itself, it's usually a simple task to move code from the component to the renderer. Listing 9-10 and Listing 9-11 show the code for the spinner component and renderer, respectively.

Listing 9-10. spinner2/WEB-INF/classes/com/corejsf/UISpinner.java
  1. package com.corejsf;  2.  3. import javax.faces.component.UIInput;  4. import javax.faces.convert.IntegerConverter;  5.  6. public class UISpinner extends UIInput {  7.    public UISpinner() {  8.       setConverter(new IntegerConverter()); // to convert the submitted value  9.       setRendererType("com.corejsf.Spinner");  // this component has a renderer 10.    } 11. } 

Listing 9-11. spinner2/WEB-INF/classes/com/corejsf/SpinnerRenderer.java
   1. package com.corejsf;   2.   3. import java.io.IOException;   4. import java.util.Map;   5. import javax.faces.component.UIComponent;   6. import javax.faces.component.EditableValueHolder;   7. import javax.faces.component.UIInput;   8. import javax.faces.context.FacesContext;   9. import javax.faces.context.ResponseWriter;  10. import javax.faces.convert.ConverterException;  11. import javax.faces.render.Renderer;  12.  13. public class SpinnerRenderer extends Renderer {  14.    private static final String MORE = ".more";  15.    private static final String LESS = ".less";  16.  17.    public Object getConvertedValue(FacesContext context, UIComponent component,  18.          Object submittedValue) throws ConverterException {  19.       return com.corejsf.util.Renderers.getConvertedValue(context, component,  20.          submittedValue);  21.    }  22.  23.    public void encodeBegin(FacesContext context, UIComponent spinner)  24.          throws IOException {  25.       ResponseWriter writer = context.getResponseWriter();  26.       String clientId = spinner.getClientId(context);  27.  28.       encodeInputField(spinner, writer, clientId);  29.       encodeDecrementButton(spinner, writer, clientId);  30.       encodeIncrementButton(spinner, writer, clientId);  31.    }  32.  33.    public void decode(FacesContext context, UIComponent component) {  34.       EditableValueHolder spinner = (EditableValueHolder) component;  35.       Map requestMap = context.getExternalContext().getRequestParameterMap();  36.       String clientId = component.getClientId(context);  37.  38.       int increment;  39.       if (requestMap.containsKey(clientId + MORE)) increment = 1;  40.       else if (requestMap.containsKey(clientId + LESS)) increment = -1;  41.       else increment = 0;  42.  43.       try {  44.          int submittedValue  45.             = Integer.parseInt((String) requestMap.get(clientId));  46.  47.          int newValue = getIncrementedValue(component, submittedValue,  48.             increment);  49.          spinner.setSubmittedValue("" + newValue);  50.          spinner.setValid(true);  51.       }  52.       catch(NumberFormatException ex) {  53.          // let the converter take care of bad input, but we still have  54.          // to set the submitted value, or the converter won't have  55.          // any input to deal with  56.          spinner.setSubmittedValue((String) requestMap.get(clientId));  57.       }  58.    }  59.  60.    private void encodeInputField(UIComponent spinner, ResponseWriter writer,  61.          String clientId) throws IOException {  62.       writer.startElement("input", spinner);  63.       writer.writeAttribute("name", clientId, "clientId");  64.  65.       Object v = ((UIInput)spinner).getValue();  66.       if(v != null)  67.          writer.writeAttribute("value", v.toString(), "value");  68.  69.       Integer size = (Integer)spinner.getAttributes().get("size");  70.       if(size != null)  71.          writer.writeAttribute("size", size, "size");  72.  73.       writer.endElement("input");  74.    }  75.  76.    private void encodeDecrementButton(UIComponent spinner,  77.          ResponseWriter writer, String clientId) throws IOException {  78.       writer.startElement("input", spinner);  79.       writer.writeAttribute("type", "submit", null);  80.       writer.writeAttribute("name", clientId + LESS, null);  81.       writer.writeAttribute("value", "<", "value");  82.       writer.endElement("input");  83.    }  84.  85.    private void encodeIncrementButton(UIComponent spinner,  86.          ResponseWriter writer, String clientId) throws IOException {  87.       writer.startElement("input", spinner);  88.       writer.writeAttribute("type", "submit", null);  89.       writer.writeAttribute("name", clientId + MORE, null);  90.       writer.writeAttribute("value", ">", "value");  91.       writer.endElement("input");  92.    }  93.  94.    private int getIncrementedValue(UIComponent spinner, int submittedValue,  95.          int increment) {  96.       Integer minimum = (Integer) spinner.getAttributes().get("minimum");  97.       Integer maximum = (Integer) spinner.getAttributes().get("maximum");  98.       int newValue = submittedValue + increment;  99. 100.       if ((minimum == null || newValue >= minimum.intValue()) && 101.          (maximum == null || newValue <= maximum.intValue())) 102.          return newValue; 103.       else 104.          return submittedValue; 105.    } 106. } 

Calling Converters from External Renderers

If you compare Listing 9-10 and Listing 9-11 with Listing 9-1, you'll see that we moved most of the code from the original component class to a new renderer class.

However, there is a hitch. As you can see from Listing 9-10, the spinner handles conversions simply by invoking setConverter() in its constructor. Because the spinner is an input component, its superclass UIInput uses the specified converter during the "Process Validations" phase of the life cycle.

But when the spinner delegates to a renderer, it's the renderer's responsibility to convert the spinner's value by overriding Renderer.getConvertedValue(). So we must replicate the conversion code from UIInput in a custom renderer. We placed that code which is required in all renderers that use a converter in the static getConvertedValue method of the class com.corejsf.util.Renderers (see Listing 9-12 on page 392).

NOTE

graphics/note_icon.gif

The Renderers.getConvertedValue method shown in Listing 9-12 is a necessary evil because UIInput does not make its conversion code publicly available. That code resides in UIInput.validate, which looks like this in the JSF 1.0 Reference Implementation:

 

 // This code is from the javax.faces.component.UIInput class: public void validate(FacesContext context) {     if (renderer != null) {         newValue = renderer.getConvertedValue(context, this,                                                        submittedValue);     } else if (submittedValue instanceof String) {         // If there's no Renderer and we've got a String,         // run it through the Converter (if any)         Converter converter = getConverterWithType(context);         ...         // much more code follows for converting the UIInput's value         // and for dealing with conversion failures...     } } 

Because UIInput's conversion code is buried in the validate method, it's not available for a renderer to reuse, as would be the case, for example, if UIInput implemented that code in a public getConvertedValue method. Because UIInput's conversion code can't be reused, you must reimplement it for custom components that use standard converters to convert their values. Fortunately, we've already done it for you.


Supporting Value Change Listeners

If your custom component is an input component, you can fire value change events to interested listeners. For example, in a calendar application, you may want to update another component whenever a month spinner value changes.

Fortunately, it is easy to support value change listeners. The UIInput class automatically generates value change events whenever the input value has changed. Recall that there are two ways of attaching a value change listener. You can add one or more listeners with f:valueChangeListener, like this:

 

 <corejsf:spinner ...>     <f:valueChangeListener type="com.corejsf.SpinnerListener"/>     ... </corejsf:spinner> 

Or you can use a valueChangeListener attribute:

 

 <corejsf:spinner value="#{cardExpirationDate.month}"     minimum="1" maximum="12" size="3"    valueChangeListener="#{cardExpirationDate.changeListener}"/> 

The first way doesn't require any effort on the part of the component implementor. The second way merely requires that your tag handler supports the valueChangeListener attribute. The attribute value is a method binding that requires special handling the topic of the next section.

Supporting Method Bindings

Four commonly used attributes require method bindings see Table 9-2. You create a MethodBinding object by calling the createMethodBinding method of the Application class. That method has two parameters: the method binding expression and an array of Class objects that describe the method's parameter types. For example, this code creates a method binding for a value change listener:

 

 FacesContext context = FacesContext.getCurrentInstance(); Application app = context.getApplication(); Class[] paramTypes = new Class[] { ValueChangeListener.class }; MethodBinding mb = app.createMethodBinding(attributeValue, paramTypes); 

Table 9-2. Method Binding Attributes

Attribute Name

Method Parameters

valueChangeListener

ValueChangeEvent

validator

FacesContext, UIComponent, Object

actionListener

ActionEvent

action

none


You then store the MethodBinding object with the component in the usual way:

 

 component.getAttributes().put("valueChangeListener", mb); 

Alternatively, you can call the property setter directly:

 

 ((EditableValueHolder) component).setValueChangeListener(mb); 

Nobody likes to write this tedious code, so we bundled it with the setValueChangeListener method of the convenience class com.corejsf.util.Tags (see Listing 9-13 on page 394). The SpinnerTag class simply calls

 

 com.corejsf.util.Tags.setValueChangeListener(component,    valueChangeListener); 

Action listeners and validators follow exactly the same pattern see the setActionListener and setValidator methods in the com.corejsf.util.Tags class.

However, actions are slightly more complex. An action can either be a method binding or a fixed string, for example

 

 <h:commandButton value="Login" action="#{loginController.verifyUser}"/> 

or

 

 <h:commandButton value="Login" action="login"/> 

But the setAction method of the ActionSource interface requires a MethodBinding in all cases. Therefore, we must construct a MethodBinding object whose getExpressionString method returns the given string see the setAction method of the Tags class.

In the next sample program, we demonstrate the value change listener by keeping a count of all value changes that we display on the form (see Figure 9-6).

 

 public class CreditCardExpiration {    private int changes = 0;    // to demonstrate the value change listener    public void changeListener(ValueChangeEvent e) {         changes++;    } } 

Figure 9-6. Counting the Value Changes

graphics/09fig06.jpg


Figure 9-7 shows the directory structure of the sample application. As you can see, we rely on the Core JSF Renderers and Tags convenience classes that contain much of the repetitive code. (The Renderers class also contains a getSelectedItems method that we need later in this chapter ignore it for now.) Listing 9-14 contains the revised SpinnerTag class, and Listing 9-15 shows the faces-config.xml file.

Listing 9-12. spinner2/WEB-INF/classes/com/corejsf/util/Renderers.java
  1. package com.corejsf.util;  2.  3. import java.util.ArrayList;  4. import java.util.Arrays;  5. import java.util.Collection;  6. import java.util.Iterator;  7. import java.util.List;  8. import java.util.Map;  9. 10. import javax.faces.application.Application; 11. import javax.faces.component.UIComponent; 12. import javax.faces.component.UIForm; 13. import javax.faces.component.UISelectItem; 14. import javax.faces.component.UISelectItems; 15. import javax.faces.component.ValueHolder; 16. import javax.faces.context.FacesContext; 17. import javax.faces.convert.Converter; 18. import javax.faces.convert.ConverterException; 19. import javax.faces.el.ValueBinding; 20. import javax.faces.model.SelectItem; 21. 22. public class Renderers { 23.    public static Object getConvertedValue(FacesContext context, 24.       UIComponent component, 25.          Object submittedValue) throws ConverterException { 26.       if (submittedValue instanceof String) { 27.          Converter converter = getConverter(context, component); 28.          if (converter != null) { 29.             return converter.getAsObject(context, component, 30.                   (String) submittedValue); 31.          } 32.       } 33.       return submittedValue; 34.    } 35. 36.    public static Converter getConverter(FacesContext context, 37.       UIComponent component) { 38.       if (!(component instanceof ValueHolder)) return null; 39.       ValueHolder holder = (ValueHolder) component; 40. 41.       Converter converter = holder.getConverter(); 42.       if (converter != null) return converter; 43. 44.       ValueBinding valueBinding = component.getValueBinding("value"); 45.       if (valueBinding == null) return null; 46. 47.       Class targetType = valueBinding.getType(context); 48.       if (targetType == null) return null; 49.       // Version 1.0 of the reference implementation will not apply a converter 50.       // if the target type is String or Object, but that is a bug. 51. 52.       Application app = context.getApplication(); 53.       return app.createConverter(targetType); 54.    } 55. 56.    public static String getFormId(FacesContext context, UIComponent component) { 57.       UIComponent parent = component; 58.       while (!(parent instanceof UIForm)) parent = parent.getParent(); 59.       return parent.getClientId(context); 60.    } 61. 62.    public static List getSelectItems(UIComponent component) { 63.       ArrayList list = new ArrayList(); 64.       Iterator children = component.getChildren().iterator(); 65.       while (children.hasNext()) { 66.          UIComponent child = (UIComponent) children.next(); 67. 68.          if (child instanceof UISelectItem) { 69.             Object value = ((UISelectItem) child).getValue(); 70.             if (value == null) { 71.                UISelectItem item = (UISelectItem) child; 72.                list.add(new SelectItem(item.getItemValue(), 73.                      item.getItemLabel(), 74.                      item.getItemDescription(), 75.                      item.isItemDisabled())); 76.             } else if (value instanceof SelectItem) { 77.                list.add(value); 78.             } 79.          } else if (child instanceof UISelectItems) { 80.             Object value = ((UISelectItems) child).getValue(); 81.             if (value instanceof SelectItem) 82.                list.add(value); 83.             else if (value instanceof SelectItem[]) 84.                list.addAll(Arrays.asList((SelectItem[]) value)); 85.             else if (value instanceof Collection) 86.                list.addAll((Collection) value); 87.             else if (value instanceof Map) { 88.                Iterator entries = ((Map) value).entrySet().iterator(); 89.                while (entries.hasNext()) { 90.                   Map.Entry entry = (Map.Entry) entries.next(); 91.                   list.add(new SelectItem(entry.getKey(), 92.                         "" + entry.getValue())); 93.                } 94.             } 95.          } 96.       } 97.       return list; 98.    } 99. } 

Listing 9-13. spinner2/WEB-INF/classes/com/corejsf/util/Tags.java
   1. package com.corejsf.util;   2.   3. import java.io.Serializable;   4. import javax.faces.application.Application;   5. import javax.faces.component.UIComponent;   6. import javax.faces.context.FacesContext;   7. import javax.faces.el.MethodBinding;   8. import javax.faces.el.ValueBinding;   9. import javax.faces.event.ActionEvent;  10. import javax.faces.event.ValueChangeEvent;  11. import javax.faces.webapp.UIComponentTag;  12.  13. public class Tags {  14.    public static void setString(UIComponent component, String attributeName,  15.          String attributeValue) {  16.       if (attributeValue == null)  17.          return;  18.       if (UIComponentTag.isValueReference(attributeValue))  19.          setValueBinding(component, attributeName, attributeValue);  20.       else  21.          component.getAttributes().put(attributeName, attributeValue);  22.    }  23.  24.    public static void setInteger(UIComponent component,  25.          String attributeName, String attributeValue) {  26.       if (attributeValue == null) return;  27.       if (UIComponentTag.isValueReference(attributeValue))  28.          setValueBinding(component, attributeName, attributeValue);  29.       else  30.          component.getAttributes().put(attributeName,  31.                new Integer(attributeValue));  32.    }  33.  34.    public static void setBoolean(UIComponent component,  35.          String attributeName, String attributeValue) {  36.       if (attributeValue == null) return;  37.       if (UIComponentTag.isValueReference(attributeValue))  38.          setValueBinding(component, attributeName, attributeValue);  39.       else  40.          component.getAttributes().put(attributeName,  41.                new Boolean(attributeValue));  42.    }  43.  44.    public static void setValueBinding(UIComponent component, String attributeName,  45.          String attributeValue) {  46.       FacesContext context = FacesContext.getCurrentInstance();  47.       Application app = context.getApplication();  48.       ValueBinding vb = app.createValueBinding(attributeValue);  49.       component.setValueBinding(attributeName, vb);  50.    }  51.  52.    public static void setActionListener(UIComponent component,  53.       String attributeValue) {  54.       setMethodBinding(component, "actionListener", attributeValue,  55.             new Class[] { ActionEvent.class });  56.    }  57.  58.    public static void setValueChangeListener(UIComponent component,  59.          String attributeValue) {  60.       setMethodBinding(component, "valueChangeListener", attributeValue,  61.             new Class[] { ValueChangeEvent.class });  62.    }  63.  64.    public static void setValidator(UIComponent component,  65.          String attributeValue) {  66.       setMethodBinding(component, "validator", attributeValue,  67.           new Class[] { FacesContext.class, UIComponent.class, Object.class });  68.    }  69.  70.    public static void setAction(UIComponent component, String attributeValue) {  71.       if (attributeValue == null) return;  72.       if (UIComponentTag.isValueReference(attributeValue))  73.          setMethodBinding(component, "action", attributeValue,  74.                new Class[] {});  75.       else {  76.          FacesContext context = FacesContext.getCurrentInstance();  77.          Application app = context.getApplication();  78.          MethodBinding mb = new ActionMethodBinding(attributeValue);  79.          component.getAttributes().put("action", mb);  80.       }  81.    }  82.  83.    public static void setMethodBinding(UIComponent component, String attributeName,  84.          String attributeValue, Class[] paramTypes) {  85.       if (attributeValue == null)  86.          return;  87.       if (UIComponentTag.isValueReference(attributeValue)) {  88.          FacesContext context = FacesContext.getCurrentInstance();  89.          Application app = context.getApplication();  90.          MethodBinding mb = app.createMethodBinding(attributeValue, paramTypes);  91.          component.getAttributes().put(attributeName, mb);  92.       }  93.    }  94.  95.    private static class ActionMethodBinding  96.          extends MethodBinding implements Serializable {  97.       private String result;  98.  99.       public ActionMethodBinding(String result) { this.result = result; } 100.      public Object invoke(FacesContext context, Object params[]) { 101.         return result; 102.       } 103.      public String getExpressionString() { return result; } 104.      public Class getType(FacesContext context) { return String.class; } 105.   } 106. } 

Listing 9-14. spinner2/WEB-INF/classes/com/corejsf/SpinnerTag.java
  1. package com.corejsf;  2.  3. import javax.faces.component.UIComponent;  4. import javax.faces.webapp.UIComponentTag;  5.  6. public class SpinnerTag extends UIComponentTag {  7.    private String minimum = null;  8.    private String maximum = null;  9.    private String size = null; 10.    private String value = null; 11.    private String valueChangeListener = null; 12. 13.    public String getRendererType() { return "com.corejsf.Spinner"; } 14.    public String getComponentType() { return "com.corejsf.Spinner"; } 15. 16.    public void setMinimum(String newValue) { minimum = newValue; } 17.    public void setMaximum(String newValue) { maximum = newValue; } 18.    public void setSize(String newValue) { size = newValue; } 19.    public void setValue(String newValue) { value = newValue; } 20.    public void setValueChangeListener(String newValue)  { 21.       valueChangeListener = newValue; 22.    } 23. 24.    public void setProperties(UIComponent component) { 25.       // always call the superclass method 26.       super.setProperties(component); 27. 28.       com.corejsf.util.Tags.setInteger(component, "size", size); 29.       com.corejsf.util.Tags.setInteger(component, "minimum", minimum); 30.       com.corejsf.util.Tags.setInteger(component, "maximum", maximum); 31.       com.corejsf.util.Tags.setString(component, "value", value); 32.       com.corejsf.util.Tags.setValueChangeListener(component, 33.             valueChangeListener); 34.    } 35. 36.    public void release() { 37.       // always call the superclass method 38.       super.release(); 39. 40.       minimum = null; 41.       maximum = null; 42.       size = null; 43.       value = null; 44.       valueChangeListener = null; 45.    } 46. } 

Listing 9-15. spinner2/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.   <managed-bean> 26.      <managed-bean-name>cardExpirationDate</managed-bean-name> 27.      <managed-bean-class>com.corejsf.CreditCardExpiration</managed-bean-class> 28.      <managed-bean-scope>session</managed-bean-scope> 29.   </managed-bean> 30. 31.    <component> 32.       <component-type>com.corejsf.Spinner</component-type> 33.       <component-class>com.corejsf.UISpinner</component-class> 34.    </component> 35. 36.    <render-kit> 37.       <renderer> 38.          <component-family>javax.faces.Input</component-family> 39.          <renderer-type>com.corejsf.Spinner</renderer-type> 40.          <renderer-class>com.corejsf.SpinnerRenderer</renderer-class> 41.       <renderer> 42.    <render-kit> 43. </faces-config> 

 

graphics/api_icon.gif

 

 javax.faces.context.FacesContext 

  • Application getApplication()

    Returns a reference to the application object.

 

graphics/api_icon.gif

 

 javax.faces.application.Application 

  • ValueBinding createMethodBinding(String valueReferenceExpression, Class[] arguments)

    Creates a method binding and stores it in the application. The valueReferenceExpression must be a value reference expression. The Class[] represents the types of the arguments passed to the method.

 

graphics/api_icon.gif

 

 javax.faces.component.EditableValueHolder 

  • void setValueChangeListener(MethodBinding listenerMethod)

    Sets a method binding for a component that implements the EditableValueHolder interface. That method must return void and is passed a ValueChangeEvent.

 

graphics/api_icon.gif

 

 javax.faces.event.ValueChangeEvent 

  • Object getOldValue()

    Returns the component's old value.

  • Object getNewValue()

    Returns the component's new value.

 

graphics/api_icon.gif

 

 javax.faces.component.ValueHolder 

  • Converter getConverter()

    Returns the converter associated with a component. The ValueHolder interface is implemented by input and output components.

 

graphics/api_icon.gif

 

 javax.faces.component.UIComponent 

  • ValueBinding getValueBinding(String valueBindingName)

    Returns a value binding previously set by calling UIComponent.setValueBinding(). That method is discussed on page 376?.

 

graphics/api_icon.gif

 

 javax.faces.el.ValueBinding 

  • Class getType(FacesContext context) throws EvaluationException, PropertyNotFoundException

    Returns the class of the object to which a value binding applies. That class can subsequently be used to access a converter with Application.createConverter().

 

graphics/api_icon.gif

 

 javax.faces.application.Application 

  • Converter createConverter(Class targetClass) throws FacesException, NullPointerException

    Creates a converter, given its target class. JSF implementations maintain a map of valid converter types, which are typically specified in a faces configuration file. If targetClass is a key in that map, this method creates an instance of the associated converter (specified as the value for the targetClass key) and returns it. If targetClass is not in the map, this method searches the map for a key that corresponds to targetClass's interfaces and superclasses, in that order, until it finds a matching class. Once a matching class is found, this method creates an associated converter and returns it. If no converter is found for the targetClass, it's interfaces, or it's superclasses, this method returns null.

Figure 9-7. Directory Structure of the Revisited Spinner Example

graphics/09fig07.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