dev@javaserverfaces.java.net

Review Request

From: Jim Driscoll <Jim.Driscoll_at_Sun.COM>
Date: Tue, 02 Dec 2008 12:53:42 -0800

I'm changing spec'd APIs, so I'm asking for a review, preferably from Roger.

This is a change to allow error and event callbacks on the Ajax tag.




<< ADD DESCRIPTION HERE >>


SECTION: Modified Files
----------------------------
M jsf-api/src/javax/faces/component/AjaxBehavior.java
M jsf-api/src/javax/faces/component/AjaxBehaviors.java
M jsf-ri/src/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java
M jsf-ri/src/com/sun/faces/renderkit/RenderKitUtils.java

Add event and error parameter to AjaxBehavior.
Change events parameter to facesEvents in AjaxBehavior


M jsf-ri/systest/src/com/sun/faces/ajax/AjaxRequestTestCase.java
M jsf-ri/systest/src/com/sun/faces/ajax/AjaxTagTestCase.java
A + jsf-ri/systest/web/ajax/ajaxEvent.xhtml
A + jsf-ri/systest/web/ajax/ajaxTagEvent.xhtml

Add tests for event param.

SECTION: Diffs
----------------------------
===================================================================
--- jsf-api/src/javax/faces/component/AjaxBehavior.java (revision 5978)
+++ jsf-api/src/javax/faces/component/AjaxBehavior.java (working copy)
@@ -84,29 +84,59 @@
      */
     public static final String AJAX_VALUE_CHANGE_ACTION = "all";
 
- private String events = null;
+ private String error = null;
 
+ private String event = null;
+
+ private String facesEvent = null;
+
     private String execute = null;
 
     private String render = null;
 
- public AjaxBehavior(String events, String execute, String render) {
- this.events = events;
+ public AjaxBehavior(String facesEvent, String event, String error, String execute, String render) {
+ this.error = error;
+ this.event = event;
+ this.facesEvent = facesEvent;
         this.execute = execute;
         this.render = render;
     }
 
     /**
- * <p class="changed_added_2_0">Return the Ajax events associated with
+ * <p class="changed_added_2_0">Return the Faces event associated with
      * this instance.</p>
      *
      * @since 2.0
      */
- public String getEvents() {
- return events;
+ public String getFacesEvent() {
+ return facesEvent;
     }
-
+
     /**
+ * <p class="changed_added_2_0">Return the <code>String</code> of
+ * JavaScript function name that will be used to identify
+ * the client callback function that should be run in the event of
+ * an error.
+ *
+ * @since 2.0
+ */
+ public String getError() {
+ return error;
+ }
+
+ /**
+ * <p class="changed_added_2_0">Return the <code>String</code> of
+ * JavaScript function name that will be used to identify the
+ * client callback function that should be run on the occurance
+ * of a client-side event.
+ *
+ * @since 2.0
+ */
+ public String getEvent() {
+ return event;
+ }
+
+ /**
      * <p class="changed_added_2_0">Return the <code>String</code> of component
      * identifiers that will be used to identify components that should be
      * processed during the <code>execute</code> phase of the request
Index: jsf-api/src/javax/faces/component/AjaxBehaviors.java
===================================================================
--- jsf-api/src/javax/faces/component/AjaxBehaviors.java (revision 5978)
+++ jsf-api/src/javax/faces/component/AjaxBehaviors.java (working copy)
@@ -76,8 +76,8 @@
         AjaxBehavior ajaxBehavior = null;
         for (int i=ajaxBehaviors.size()-1; i>=0; i--) {
             AjaxBehavior behavior = ajaxBehaviors.get(i);
- if (behavior.getEvents() == null ||
- behavior.getEvents().equals(eventName)) {
+ if (behavior.getFacesEvent() == null ||
+ behavior.getFacesEvent().equals(eventName)) {
                 ajaxBehavior = behavior;
             }
         }
 
Index: jsf-ri/src/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java
===================================================================
--- jsf-ri/src/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java (revision 5978)
+++ jsf-ri/src/com/sun/faces/facelets/tag/jsf/core/AjaxHandler.java (working copy)
@@ -91,26 +91,26 @@
  * of {_at_link javax.faces.component.AjaxBehavior} using the tag attribute
  * values. If this tag is nested within a single
  * {_at_link javax.faces.component.ActionSource} component, and the
- * <code>events</code> attribute value is not specified or is
+ * <code>facesEvent</code> attribute value is not specified or is
  * one of the following:
  * <ul>
  * <li>{_at_link javax.faces.component.AjaxBehavior#AJAX_ACTION}</li>
- * <li>{_at_link javax.faces.component.AjaxBehaviorx#AJAX_ACTION_VALUE_CHANGE}</li>
+ * <li>{_at_link javax.faces.component.AjaxBehavior#AJAX_VALUE_CHANGE_ACTION}</li>
  * </ul>
  * put the {_at_link javax.faces.component.AjaxBehavior} instance in the parent
  * component's attribute <code>Map</code> under the key
  * {_at_link javax.faces.component.AJAX_BEHAVIOR}. If this tag is nested within
  * a single {_at_link javax.faces.component.EditableValueHolder} component,
- * and the <code>events</code> attribute value is not specified or is
+ * and the <code>facesEvent</code> attribute value is not specified or is
  * one of the following:
  * <ul>
  * <li>{_at_link javax.faces.component.AjaxBehavior#AJAX_VALUE_CHANGE}</li>
- * <li>{_at_link javax.faces.component.AjaxBehaviorx#AJAX_ACTION_VALUE_CHANGE}</li>
+ * <li>{_at_link javax.faces.component.AjaxBehavior#AJAX_VALUE_CHANGE_ACTION}</li>
  * </ul>
  * put the {_at_link javax.faces.component.AjaxBehavior} instance in the parent
  * component's attribute <code>Map</code> under the key
  * {_at_link javax.faces.component.AJAX_BEHAVIOR}.
- * Throw an <code>exception</code> if the <code>events</code> attribute value
+ * Throw an <code>exception</code> if the <code>facesEvent</code> attribute value
  * does not match the component type.
  * <br/><br/>
  * If this tag is nested within a component other than an
@@ -139,18 +139,22 @@
  */
 public final class AjaxHandler extends TagHandler {
 
- private final TagAttribute events;
+ private final TagAttribute facesEvent;
     private final TagAttribute execute;
     private final TagAttribute render;
+ private final TagAttribute event;
+ private final TagAttribute error;
 
     /**
      * @param config
      */
     public AjaxHandler(TagConfig config) {
         super(config);
- this.events = this.getAttribute("events");
+ this.facesEvent = this.getAttribute("facesEvent");
         this.execute = this.getAttribute("execute");
         this.render = this.getAttribute("render");
+ this.event = this.getAttribute("event");
+ this.error = this.getAttribute("error");
     }
 
     /*
@@ -165,42 +169,50 @@
             return;
         }
 
- String events = null;
+ String facesEvent = null;
         String execute = null;
         String render = null;
+ String event = null;
+ String error = null;
 
- if (null != this.events) {
- events = this.events.getValue(ctx);
+ if (this.facesEvent != null) {
+ facesEvent = this.facesEvent.getValue(ctx);
         }
- if (null != this.execute) {
+ if (this.execute != null) {
             execute = this.execute.getValue(ctx).replace(' ',',');
         }
- if (null != this.render) {
+ if (this.render != null) {
             render = this.render.getValue(ctx).replace(' ',',');
         }
-
- AjaxBehavior ajaxBehavior = new AjaxBehavior(events, execute, render);
+ if (this.event != null) {
+ event = this.event.getValue(ctx);
+ }
+ if (this.error != null) {
+ error = this.error.getValue(ctx);
+ }
 
+ AjaxBehavior ajaxBehavior = new AjaxBehavior(facesEvent, event, error, execute, render);
+
         //
         // If we are nested within an EditableValueHolder or ActionSource component..
         //
         if (parent instanceof ActionSource) {
- if (null == events || events.equals(AjaxBehavior.AJAX_VALUE_CHANGE_ACTION) ||
- events.equals(AjaxBehavior.AJAX_ACTION)) {
+ if (null == facesEvent || facesEvent.equals(AjaxBehavior.AJAX_VALUE_CHANGE_ACTION) ||
+ facesEvent.equals(AjaxBehavior.AJAX_ACTION)) {
                 parent.getAttributes().put(AjaxBehavior.AJAX_BEHAVIOR, ajaxBehavior);
                 installAjaxResourceIfNecessary();
                 return;
             } else {
- throw new TagAttributeException(this.events, "'Events' attribute value must be 'action' for 'ActionSource' components");
+ throw new TagAttributeException(this.facesEvent, "'facesEvent' attribute value must be 'action' for 'ActionSource' components");
             }
         } else if (parent instanceof EditableValueHolder) {
- if (null == events || events.equals(AjaxBehavior.AJAX_VALUE_CHANGE_ACTION) ||
- events.equals(AjaxBehavior.AJAX_VALUE_CHANGE)) {
+ if (null == facesEvent || facesEvent.equals(AjaxBehavior.AJAX_VALUE_CHANGE_ACTION) ||
+ facesEvent.equals(AjaxBehavior.AJAX_VALUE_CHANGE)) {
                 parent.getAttributes().put(AjaxBehavior.AJAX_BEHAVIOR, ajaxBehavior);
                 installAjaxResourceIfNecessary();
                 return;
             } else {
- throw new TagAttributeException(this.events, "'Events' attribute value must be 'valueChange' for 'EditableValueHolder' components");
+ throw new TagAttributeException(this.facesEvent, "'facesEvent' attribute value must be 'valueChange' for 'EditableValueHolder' components");
             }
         }
             
Index: jsf-ri/src/com/sun/faces/renderkit/RenderKitUtils.java
===================================================================
--- jsf-ri/src/com/sun/faces/renderkit/RenderKitUtils.java (revision 5978)
+++ jsf-ri/src/com/sun/faces/renderkit/RenderKitUtils.java (working copy)
@@ -337,6 +337,8 @@
         StringBuilder ajaxCommand = new StringBuilder(256);
         String execute = ajaxBehavior.getExecute();
         String render = ajaxBehavior.getRender();
+ String event = ajaxBehavior.getEvent();
+ String error = ajaxBehavior.getError();
         ajaxCommand.append(AJAX_REQUEST);
         ajaxCommand.append("(this, event");
         if (execute != null || render != null) {
@@ -358,6 +360,26 @@
             ajaxCommand.append(render.replace(' ', ','));
             ajaxCommand.append("'");
         }
+ if (event != null) {
+ if (already) {
+ ajaxCommand.append(",");
+ } else {
+ already = true;
+ }
+ ajaxCommand.append("event:'");
+ ajaxCommand.append(event);
+ ajaxCommand.append("'");
+ }
+ if (error != null) {
+ if (already) {
+ ajaxCommand.append(",");
+ } else {
+ already = true;
+ }
+ ajaxCommand.append("error:'");
+ ajaxCommand.append(error);
+ ajaxCommand.append("'");
+ }
         if (already) {
             ajaxCommand.append("}");
         }
Index: jsf-ri/systest/src/com/sun/faces/ajax/AjaxRequestTestCase.java
===================================================================
--- jsf-ri/systest/src/com/sun/faces/ajax/AjaxRequestTestCase.java (revision 5978)
+++ jsf-ri/systest/src/com/sun/faces/ajax/AjaxRequestTestCase.java (working copy)
@@ -438,4 +438,31 @@
         assertTrue(check(out3,"echo"));
     }
 
+ public void testAjaxEvent() throws Exception {
+ getPage("/faces/ajax/ajaxEvent.xhtml");
+ System.out.println("Start ajax event test");
+
+ // First we'll check the first page was output correctly
+ assertTrue(check("countForm:out1","0"));
+ assertTrue(check("out2","1"));
+
+ // Submit the ajax request
+ HtmlSubmitInput button1 = (HtmlSubmitInput) lastpage.getHtmlElementById("countForm:button1");
+ HtmlPage lastpage = (HtmlPage) button1.click();
+
+ // Check that the ajax request succeeds
+ assertTrue(check("countForm:out1","2"));
+
+ // Check that the request did NOT update the rest of the page.
+ assertTrue(check("out2","1"));
+
+ // Check that events were written to the page.
+ String statusArea = "Name: countForm:button1 Event: beforeOpen ";
+ statusArea = statusArea + "Name: countForm:button1 Event: onCompletion " ;
+ statusArea = statusArea + "Name: countForm:button1 Event: afterUpdate " ;
+ //System.out.println(statusArea);
+ //System.out.println(getText("statusArea"));
+ assertTrue(check("statusArea",statusArea));
+ }
+
 }
Index: jsf-ri/systest/src/com/sun/faces/ajax/AjaxTagTestCase.java
===================================================================
--- jsf-ri/systest/src/com/sun/faces/ajax/AjaxTagTestCase.java (revision 5978)
+++ jsf-ri/systest/src/com/sun/faces/ajax/AjaxTagTestCase.java (working copy)
@@ -497,5 +497,30 @@
         assertTrue(check(out3,"echo"));
 
     }
+ public void testAjaxEvent() throws Exception {
+ getPage("/faces/ajax/ajaxTagEvent.xhtml");
+ System.out.println("Start ajax tag event test");
 
+ // First we'll check the first page was output correctly
+ assertTrue(check("countForm:out1","0"));
+ assertTrue(check("out2","1"));
+
+ // Submit the ajax request
+ HtmlSubmitInput button1 = (HtmlSubmitInput) lastpage.getHtmlElementById("countForm:button1");
+ HtmlPage lastpage = (HtmlPage) button1.click();
+
+ // Check that the ajax request succeeds
+ assertTrue(check("countForm:out1","2"));
+
+ // Check that the request did NOT update the rest of the page.
+ assertTrue(check("out2","1"));
+
+ // Check that events were written to the page.
+ String statusArea = "Name: countForm:button1 Event: beforeOpen ";
+ statusArea = statusArea + "Name: countForm:button1 Event: onCompletion " ;
+ statusArea = statusArea + "Name: countForm:button1 Event: afterUpdate " ;
+ //System.out.println(statusArea);
+ //System.out.println(getText("statusArea"));
+ assertTrue(check("statusArea",statusArea));
+ }
 }
\ No newline at end of file
Index: jsf-ri/systest/web/ajax/ajaxEvent.xhtml
===================================================================
--- jsf-ri/systest/web/ajax/ajaxEvent.xhtml (revision 5978)
+++ jsf-ri/systest/web/ajax/ajaxEvent.xhtml (working copy)
@@ -10,21 +10,37 @@
 </h:head>
 <h:body>
     <h:outputScript library="javax.faces" name="ajax.js" target="head"/>
+<script type="text/javascript">
+ function statusUpdate(data) {
+ var statusArea = document.getElementById("statusArea");
+ var text = statusArea.value;
+ text = text + "Name: "+data.execute;
+ if (data.type === "event") {
+ text = text +" Event: "+data.name+" ";
+ } else { // otherwise, it's an error
+ text = text + " Error: "+data.name+" ";
+ }
+ statusArea.value = text;
+}
+</script>
     <h1>Simple Counter Test</h1>
     <h:form id="countForm">
         <h:outputText id="out1" value="#{ajaxrequest.count}"/>
         <br/>
         <!-- Increment the counter on the server, and the client -->
         <h:commandButton id="button1" value="Count"
- onclick="jsf.ajax.request(this, event, {execute: this.id, render: 'countForm:out1'}); return false;"/>
+ onclick="jsf.ajax.request(this, event, {execute: this.id, render: 'countForm:out1', event: 'statusUpdate', error: 'statusUpdate'}); return false;"/>
         <br/>
         <!-- Resets the counter, doesn't refresh the page -->
         <h:commandButton id="reset" value="reset"
- onclick="jsf.ajax.request(this, event, {execute:'countForm:reset', render: 'countForm:out1'}); return false;"
+ onclick="jsf.ajax.request(this, event, {execute:'countForm:reset', render: 'countForm:out1', event: 'statusUpdate', error: 'statusUpdate'}); return false;"
                             actionListener="#{ajaxrequest.resetCount}"/>
     </h:form>
     <!-- Contained outside the form - shouldn't update except for full page load -->
     <h:outputText id="out2" value="#{ajaxrequest.count}"/>
-
+ <p>
+ <h3> Status:</h3>
+ <textarea id="statusArea" cols="40" rows="10" readonly="readonly" />
+ </p>
 </h:body>
 </html>
\ No newline at end of file

Property changes on: jsf-ri/systest/web/ajax/ajaxEvent.xhtml
___________________________________________________________________
Added: svn:mergeinfo

Index: jsf-ri/systest/web/ajax/ajaxTagEvent.xhtml
===================================================================
--- jsf-ri/systest/web/ajax/ajaxTagEvent.xhtml (revision 5978)
+++ jsf-ri/systest/web/ajax/ajaxTagEvent.xhtml (working copy)
@@ -10,22 +10,38 @@
 </h:head>
 <h:body>
     <h:outputScript library="javax.faces" name="ajax.js" target="head"/>
+<script type="text/javascript">
+ function statusUpdate(data) {
+ var statusArea = document.getElementById("statusArea");
+ var text = statusArea.value;
+ text = text + "Name: "+data.execute;
+ if (data.type === "event") {
+ text = text +" Event: "+data.name+" ";
+ } else { // otherwise, it's an error
+ text = text + " Error: "+data.name+" ";
+ }
+ statusArea.value = text;
+}
+</script>
     <h1>Simple Counter Test</h1>
     <h:form id="countForm">
         <h:outputText id="out1" value="#{ajaxrequest.count}"/>
         <br/>
         <!-- Increment the counter on the server, and the client -->
         <h:commandButton id="button1" value="Count">
- <f:ajax render="countForm:out1"/>
+ <f:ajax render="countForm:out1" event="statusUpdate" error="statusUpdate"/>
         </h:commandButton>
         <br/>
         <!-- Resets the counter, doesn't refresh the page -->
         <h:commandButton id="reset" value="reset" actionListener="#{ajaxrequest.resetCount}">
- <f:ajax execute="countForm:reset" render="countForm:out1"/>
+ <f:ajax execute="countForm:reset" render="countForm:out1" event="statusUpdate" error="statusUpdate"/>
         </h:commandButton>
     </h:form>
     <!-- Contained outside the form - shouldn't update except for full page load -->
     <h:outputText id="out2" value="#{ajaxrequest.count}"/>
-
+ <p>
+ <h3> Status:</h3>
+ <textarea id="statusArea" cols="40" rows="10" readonly="readonly" />
+ </p>
 </h:body>
 </html>
\ No newline at end of file

Property changes on: jsf-ri/systest/web/ajax/ajaxTagEvent.xhtml
___________________________________________________________________
Added: svn:mergeinfo


SECTION: New Files
----------------------------
SEE ATTACHMENTS