Encoding JavaScript to Avoid Server Roundtrips

   

The spinner component performs a roundtrip to the server every time you click one of its buttons. That roundtrip updates the spinner's value on the server. Those roundtrips can take a severe bite out of the spinner's performance, so in almost all circumstances, it's better to store the spinner's value on the client and update the component's value only when the form in which the spinner resides is submitted. We can do that with JavaScript that looks like this:

 

 <input type="text" name="_id1:monthSpinner" value="0"/> <script language="JavaScript">    document['_id1']['_id1:monthSpinner'].spin = function (increment) {       var v = parseInt(this.value) + increment;       if (isNaN(v)) return;       if ('min' in this && v < this.min) return;       if ('max' in this && v > this.max) return;          this.value = v;    };    document['_id1']['_id1:monthSpinner'].min = 0; </script> <input type="button" value="<"       onclick="document['_id1']['_id1:monthSpinner'].spin(-1);"/> <input type="button" value=">"       onclick="document['_id1']['_id1:monthSpinner'].spin(1);"/> 

When you write JavaScript code that accesses fields in a form, you need to have access to the form ID, such as '_id1' in the expression

 

 document['_id1']['_id1:monthSpinner'] 

The second array index is simply the client ID of the component.

Obtaining the form ID is a common task, and we added a convenience method to the com.corejsf.util.Renderers class for this purpose:

 

 public static String getFormId(FacesContext context, UIComponent component) {    UIComponent parent = component;    while (!(parent instanceof UIForm)) parent = parent.getParent();    return parent.getClientId(context); } 

We won't go into the details of JavaScript programming here, but note that we are a bit paranoid about injecting global JavaScript functions into an unknown page. We don't want to risk name conflicts. Fortunately, JavaScript is a well-designed language with a flexible object model. Rather than writing a global spin function, we define spin to be a method of the text field object. JavaScript lets you enhance the capabilities of objects on-the-fly, simply by adding methods and fields. We use the same approach with the minimum and maximum values of the spinner, adding min and max fields if they are required.

The spinner renderer that encodes the preceding JavaScript is shown in Listing 9-16.

Note that the UISpinner component is completely unaffected by this change. Only the renderer has been updated, thus demonstrating the power of pluggable renderers.

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



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