dev@jsf-extensions.java.net

Re: [JSF-EXT] Jacob: Seeking Review: 7-Immediate

From: Dan Labrecque - Sun Microsystems <Dan.Labrecque_at_Sun.COM>
Date: Tue, 15 Aug 2006 18:05:57 -0400

Is calling setImmediate(true) on a component the only way to achieve
this behavior? I like the idea of adding an immediate flag as a property
of DynaFaces.fireAjaxTransaction, but I'm concerned that the value is
persisted via component state.

I would like immediate behavior during the AJAX request, but I do not
want this to persist for subsequent page submits. For example, I would
like to use the immediate behavior to auto-validate a text field during
an AJAX request because I do not want its value to be updated. However,
if the user clicks a button to submit the page, I expect the text field
value to be updated. If the immediate property is persisted, how will
the text field value be updated in the model?

Dan

Ed Burns wrote:
> https://jsf-extensions.dev.java.net/issues/show_bug.cgi?id=7
>
> This is checked in on branch IMMEDIATE_20060815
>
> I'd like to get everyone's feedback on this issue.
>
> Issue: DynaFaces-7-immediate
>
> This change-bundle adds the 'immediate' option to the set of supported
> options for the associative array passed as the last argument to
> DynaFaces.fireAjaxTransaction() and
> DynaFaces.installDeferredAjaxTransaction(). For example:
>
> + DynaFaces.installDeferredAjaxTransaction($('immediate'), 'click', {
> + execute: "immediate",
> + immediate: true
> + });
>
> This will end up locating the component with clientId 'immediate' and
> calling setImmediate(true) on it *if* the component is an instance of
> EditableValueHolder or ActionSource. This call is made before the
> partial tree list is traversed for processDecodes, and naturally, the
> 'immediate' property is persisted as part of the component instance's
> view state.
>
> SECTION: Changes
>
> M run-time/samples/simple-partial-update/src/main/webapp/home.jsp
>
> - modify to showcase immediate vs non-immediate usage.
>
> M run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java
>
> - copy over prefixViewTraversal from Sun JSF Impl.
>
> M run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java
>
> - add isImmediateAjaxRequest() method.
>
> + * <p>Return <code>true</code> if and only if the request headers include
> + * an entry for {_at_link #PARTIAL_HEADER} and the value for that header is
> + * <code>immediate</code>. Otherwise return <code>false</code>.
>
> M run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java
>
> - in processDecodes(), handle the case where this is an immediate request
> but no partial trees have been identified. In this case, the entire
> tree is traversed and all ActionSource or EditableValueHolder
> instances are set to immediate == true.
>
>
> - in the partial traversal ContextCallback, if this is
> APPLY_REQUEST_VALUES, and the current component instance is an
> ActionSource or EditableValueHolder, and this is an immediate request,
> set immediate == true.
>
> M run-time/avatar/src/main/resources/com_sun_faces_ajax.js
>
> - If the user specified an immediate option, set the value of the
> partial header to be 'immediate'.
>
> M pom.xml
>
> - add common and simple-partial-update to samples profile.
>
> SECTION: Diffs
>
> Index: run-time/samples/simple-partial-update/src/main/webapp/home.jsp
> ===================================================================
> --- run-time/samples/simple-partial-update/src/main/webapp/home.jsp (revision 187)
> +++ run-time/samples/simple-partial-update/src/main/webapp/home.jsp (working copy)
> @@ -42,11 +42,26 @@
>
> </tr>
>
> +<tr>
> +
> +<td>Same as above but with immediate set to true.</td>
> +
> +<td><h:commandButton id="immediate" actionListener="#{testBean.changeText}"
> + value="submit"/>
> +</td>
> +
> +</tr>
> +
> +
> </table>
>
> </h:form>
> <script type='text/javascript'>
> DynaFaces.installDeferredAjaxTransaction($('button'), 'click');
> + DynaFaces.installDeferredAjaxTransaction($('immediate'), 'click', {
> + execute: "immediate",
> + immediate: true
> + });
> </script>
>
>
> Index: run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java
> ===================================================================
> --- run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java (revision 187)
> +++ run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java (working copy)
> @@ -25,7 +25,9 @@
>
> package com.sun.faces.extensions.common.util;
>
> +import java.util.Iterator;
> import java.util.List;
> +import javax.faces.FacesException;
> import javax.faces.FactoryFinder;
> import javax.faces.application.Application;
> import javax.faces.component.UIComponent;
> @@ -137,4 +139,25 @@
> RenderKit curRenderKit = fact.getRenderKit(context, renderKitId);
> return curRenderKit;
> }
> +
> + public static boolean prefixViewTraversal(FacesContext context,
> + UIComponent root,
> + TreeTraversalCallback action) throws FacesException {
> + boolean keepGoing = false;
> + if (keepGoing = action.takeActionOnNode(context, root)) {
> + Iterator<UIComponent> kids = root.getFacetsAndChildren();
> + while (kids.hasNext() && keepGoing) {
> + keepGoing = prefixViewTraversal(context,
> + kids.next(),
> + action);
> + }
> + }
> + return keepGoing;
> + }
> +
> + public static interface TreeTraversalCallback {
> + public boolean takeActionOnNode(FacesContext context,
> + UIComponent curNode) throws FacesException;
> + }
> +
> }
> Index: run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java
> ===================================================================
> --- run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java (revision 187)
> +++ run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java (working copy)
> @@ -314,7 +314,32 @@
> }
> return result;
> }
> +
> + /**
> + * <p>Return <code>true</code> if and only if the request headers include
> + * an entry for {_at_link #PARTIAL_HEADER} and the value for that header is
> + * <code>immediate</code>. Otherwise return <code>false</code>.
> + */
>
> + public static boolean isImmediateAjaxRequest() {
> + ExternalContext ext = FacesContext.getCurrentInstance().getExternalContext();
> + Map<String, Object> requestMap = ext.getRequestMap();
> + final String immediateFlag = FACES_PREFIX + "IsImmediate";
> + if (requestMap.containsKey(immediateFlag)) {
> + return true;
> + }
> +
> + Map<String, String> p = ext.getRequestHeaderMap();
> + String partialValue = p.get(PARTIAL_HEADER);
> + boolean result = false;
> + if (null != partialValue) {
> + result = partialValue.equalsIgnoreCase("immediate");
> + }
> + if (result) {
> + requestMap.put(immediateFlag, Boolean.TRUE);
> + }
> + return result;
> + }
> private Object origResponse = null;
>
> /**
> Index: run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java
> ===================================================================
> --- run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java (revision 187)
> +++ run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java (working copy)
> @@ -31,13 +31,16 @@
>
> import com.sun.faces.extensions.avatar.event.EventParser;
> import com.sun.faces.extensions.avatar.lifecycle.AsyncResponse;
> +import com.sun.faces.extensions.common.util.Util;
> import java.io.IOException;
> import java.io.Serializable;
> import java.util.Iterator;
> import java.util.List;
> import javax.faces.FacesException;
> import javax.faces.application.FacesMessage;
> +import javax.faces.component.ActionSource;
> import javax.faces.component.ContextCallback;
> +import javax.faces.component.EditableValueHolder;
> import javax.faces.component.UIComponent;
> import javax.faces.component.UIViewRoot;
> import javax.faces.component.UIViewRootCopy;
> @@ -137,11 +140,35 @@
> super.processDecodes(context);
> return;
> }
> + AsyncResponse async = AsyncResponse.getInstance();
> +
> + // If this is an immediate "render all" request and there are
> + // no explicit execute subtrees and the user didn't request
> + // execute: none...
> + if (async.isImmediateAjaxRequest() && async.isRenderAll() &&
> + async.getExecuteSubtrees().isEmpty() &&
> + !async.isExecuteNone()) {
> + // Traverse the entire view and mark every ActionSource or
> + // EditableValueHolder as immediate.
> + Util.TreeTraversalCallback markImmediate = new Util.TreeTraversalCallback() {
> + public boolean takeActionOnNode(FacesContext context, UIComponent comp) throws FacesException {
> + if (comp instanceof ActionSource) {
> + ((ActionSource)comp).setImmediate(true);
> + } else if (comp instanceof EditableValueHolder) {
> + ((EditableValueHolder)comp).setImmediate(true);
> + }
> +
> + return true;
> + }
> +
> + };
> + Util.prefixViewTraversal(context, this, markImmediate);
> + }
> +
> invokedCallback = invokeContextCallbackOnSubtrees(context,
> new PhaseAwareContextCallback(PhaseId.APPLY_REQUEST_VALUES));
>
> // Queue any events for this request in a context aware manner
> - AsyncResponse async = AsyncResponse.getInstance();
> ResponseWriter writer = null;
> try {
> writer = async.getResponseWriter();
> @@ -384,15 +411,22 @@
> ConverterException converterException = null;
>
> if (curPhase == PhaseId.APPLY_REQUEST_VALUES) {
> + // If the user requested an immediate request
> + // Make sure to set the immediate flag.
> + if (AsyncResponse.isImmediateAjaxRequest()) {
> + if (comp instanceof ActionSource) {
> + ((ActionSource)comp).setImmediate(true);
> + } else if (comp instanceof EditableValueHolder) {
> + ((EditableValueHolder)comp).setImmediate(true);
> + }
> + }
> +
> comp.processDecodes(facesContext);
> - }
> - else if (curPhase == PhaseId.PROCESS_VALIDATIONS) {
> + } else if (curPhase == PhaseId.PROCESS_VALIDATIONS) {
> comp.processValidators(facesContext);
> - }
> - else if (curPhase == PhaseId.UPDATE_MODEL_VALUES) {
> + } else if (curPhase == PhaseId.UPDATE_MODEL_VALUES) {
> comp.processUpdates(facesContext);
> - }
> - else if (curPhase == PhaseId.RENDER_RESPONSE) {
> + } else if (curPhase == PhaseId.RENDER_RESPONSE) {
> ResponseWriter writer = AsyncResponse.getInstance().getResponseWriter();
>
> writer.startElement("render", comp);
> Index: run-time/avatar/src/main/resources/com_sun_faces_ajax.js
> ===================================================================
> --- run-time/avatar/src/main/resources/com_sun_faces_ajax.js (revision 187)
> +++ run-time/avatar/src/main/resources/com_sun_faces_ajax.js (working copy)
> @@ -481,7 +481,12 @@
>
> // guarantee our header
> this.options.requestHeaders.push(gPartial);
> - this.options.requestHeaders.push('true');
> + if (this.options.immediate) {
> + this.options.requestHeaders.push('immediate');
> + }
> + else {
> + this.options.requestHeaders.push('true');
> + }
>
> // add methodName
> if (this.options.methodName) {
> Index: pom.xml
> ===================================================================
> --- pom.xml (revision 187)
> +++ pom.xml (working copy)
> @@ -145,11 +145,13 @@
> <profile>
> <id>samples</id>
> <modules>
> + <module>run-time/common</module>
> <module>run-time/avatar</module>
> <module>run-time/samples/j1</module>
> <module>run-time/samples/cardemo</module>
> <module>run-time/samples/jmaki</module>
> <module>run-time/samples/simple-events</module>
> + <module>run-time/samples/simple-partial-update</module>
> </modules>
> </profile>
> <profile>
>
>
>