tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject svn commit: r751140 - in /tapestry/tapestry5/trunk: src/site/apt/guide/ tapestry-core/src/main/java/org/apache/tapestry5/ajax/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ tapestry-core/src/main/java/org/apache/tapestry5/interna...
Date Fri, 06 Mar 2009 23:43:42 GMT
Author: hlship
Date: Fri Mar  6 23:43:41 2009
New Revision: 751140

URL: http://svn.apache.org/viewvc?rev=751140&view=rev
Log:
TAP5-108: A component event handler for Ajax requests should have a mechanism to update mutiple
zones on the client

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/MultiZoneUpdate.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/package.html
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/CombinedRenderCommand.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/package.html
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MultiZoneUpdateDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateDemo.java
Modified:
    tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueue.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/ajax.apt Fri Mar  6 23:43:41 2009
@@ -7,7 +7,7 @@
   Tapestry includes sophisticated JavaScript and Ajax support, based on the
   {{{http://www.prototypejs.org/}Prototype}} and
   {{{http://script.aculo.us/}Scriptaculous}} libraries.  These libraries are
-  all packaged with Tapestry itself ... no extra download required.
+  all packaged with Tapestry itself ... no extra download is required.
 
   The goal for Tapestry is to have many basic and useful components available within the
   application itself, and make it easy for other JavaScript widgets to be encapsulated as
@@ -222,22 +222,24 @@
 * Zone
 
   Initial support for Zones is now in place.  Zones are Tapestry's approach to performing
partial updates to
-  the client side.  A Zone component renders as a \<div\> element with the "t-zone"
CSS class.  It also
-  adds some JavaScript to the page to "wire up" a Tapestry.Zone object to control updating
+  the client side.  A Zone component renders as a\<div\> element with the "t-zone"
CSS class (it can actually render
+  as any element you chose, but for discussion's sake, we'll assume the \<div\> default).
 It also
+  adds some JavaScript to the page to "wire up" a Tapestry.ZoneManager object to control
updating
   the \<div\> element.
 
-  A Zone can be updated via an ActionLink component.  ActionLink supports a zone parameter,
which is the id
+  A Zone can be updated via an ActionLink or EventLink component, or by a Form.  All of these
components
+   support a zone parameter, which is the id
   of the Zone's \<div\>. Clicking such a link will invoke an event handler method on
the server as normal ...
    except that the return value of the event handler method is used to send a <partial
page response>
    to the client, and the content of that response is used to update the Zone's \<div\>
in place.
 
 ** Zone div vs. update div
 
-  In many situations, a Zone is a kind of "wrapper" or "container" for dynamic content, that
provides
+  In many situations, a Zone is a kind of "wrapper" or "container" for dynamic content; one
that provides
   a look and feel ... a bit of wrapping markup to create a border.  In that situtation,
   the Zone \<div\> may contain an update \<div\>.
 
-  An update \<div\> is a \<div\> with the CSS class "t-zone-update", <inside>
the Zone's \<div\>.
+  An update \<div\> is specifcally a \<div\> element with the CSS class "t-zone-update",
<inside> the Zone's \<div\>.
 
   When an update occurs, the update \<div\>'s content will be changed, rather than
the entire Zone \<div\>.
 
@@ -251,7 +253,7 @@
 
   With a Zone update, the return value is used to render a <partial response> within
the <same request>.
 
-  This return value should be an injected component or block.  The value will be rendered,
and that
+  This return value is typically an injected component or block.  The value will be rendered,
and that
   markup will be used on the client side to update the Zone's \<div\>.
 
   An event handler may return a
@@ -260,6 +262,33 @@
   Returning a page name (as a String), or a page class, or a page instance will also send
a redirect
   to the indicated page.
 
+** Multiple Zone Updates
+
+  An event handler may cause multiple zones to be updated on the client side. To accomplish
this, return
+  a {{{../apidocs/org/apache/tapestry5/ajax/MultiZoneUpdate.html}MultiZoneUpdate}} object
configured
+  with the zones to update.  You must know the client-side id for each zone to update (the
best
+  way for this is to lock down the zone's id using the id parameter of the Zone component).
+
+  The renderer for each zone can be a block or component, or a
+  {{{../apidocs/org/apache/tapestry5/Renderable.html}Renderable}} or
+  {{{../apidocs/org/apache/tapestry5/runtime/RenderCommand.html}RenderCommand}} ... or an
object,
+  such as String, that can be coerced to either of these.  Typically, you will inject a Block
or Component and return that:
+  
+----
+  @Inject
+  private Form registrationForm;
+
+  @Inject Block registrationHelp;
+
+  Object onActionFromRegister()
+  {
+    return new MultiZoneUpdate("userInput", registrationForm).add("helpPanel", registrationHelp);
+  }
+----
+
+  This implies that there are two zones, "userInput" and "helpPanel", somewhere in the rendered
page,
+  waiting to receive the updated content.
+
 ** Graceful Degradation
 
   Users who do not have JavaScript enabled may click ActionLinks that are configured to update
a Zone.
@@ -342,7 +371,7 @@
   If your page loads slowly (typically, because of scripts loaded from external sites), you
may see a race
   condition where the user can click on a link before an event handler for that link has
been wired up.
 
-  The client-side function <<<Tapestry.waitForPage()>> can be used in an element's
onclick handler to force
+  The client-side function <<<Tapestry.waitForPage()>>> can be used in
an element's onclick handler to force
   a wait for the page to fully load. In this race condition, the screen will dim and a message
will appear
   advising the  user to wait a moment; once the page is fully loaded, this modal dialog will
be removed.
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/MultiZoneUpdate.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/MultiZoneUpdate.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/MultiZoneUpdate.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/MultiZoneUpdate.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,95 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.ajax;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+
+import java.util.Map;
+
+/**
+ * A mapping from <em>client-side zone ids</em> to objects that can render the
content for that zone on the client. An
+ * event handler method may instantiate an instance and chain together a series of calls
to {@link #add(String,
+ * Object)}, and return the final result.
+ * <p/>
+ * Remember that client-side element ids may not match server-side component ids, especially
once Ajax is added to the
+ * mix. Because of this, it is highly recommended that the client-side logic gather the actual
component ids and include
+ * those in the Ajax request, to ensure that the server generates updates that the client
can process. Better yet, use
+ * the Zone's id parameter to lock down the zone's id to a known, predictable value.
+ *
+ * @since 5.1.0.1
+ */
+public class MultiZoneUpdate
+{
+    private final MultiZoneUpdate parent;
+
+    private final String zoneId;
+
+    private final Object renderer;
+
+    public MultiZoneUpdate(String zoneId, Object renderer)
+    {
+        this(zoneId, renderer, null);
+    }
+
+    private MultiZoneUpdate(String zoneId, Object renderer, MultiZoneUpdate parent)
+    {
+        this.zoneId = Defense.notBlank(zoneId, "zoneId");
+        this.renderer = Defense.notNull(renderer, "renderer");
+
+        this.parent = parent;
+    }
+
+    /**
+     * Returns a <strong>new</strong> MultiZoneUpdate reflecting the mapping
from the indicated zone to an object that
+     * will render the content for that zone.
+     *
+     * @param zoneId   client id of zone to update
+     * @param renderer object that can provide the content for the zone
+     * @return new MultiZoneUpdate
+     */
+    public MultiZoneUpdate add(String zoneId, Object renderer)
+    {
+        return new MultiZoneUpdate(zoneId, renderer, this);
+    }
+
+    /**
+     * Returns a mapping from client zone id to renderer object for that zone.
+     *
+     * @return string to renderer map
+     */
+    public Map<String, Object> getZoneToRenderMap()
+    {
+        Map<String, Object> result = CollectionFactory.newMap();
+
+        MultiZoneUpdate cursor = this;
+
+        while (cursor != null)
+        {
+            result.put(cursor.zoneId, cursor.renderer);
+
+            cursor = cursor.parent;
+        }
+
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("MultiZoneUpdate[%s]", getZoneToRenderMap());
+    }
+}
+

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/package.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/package.html?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/package.html
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ajax/package.html
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,3 @@
+<body>
+Extra utility classes used to support Ajax interactions between the client and the browser.
+</body>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java
Fri Mar  6 23:43:41 2009
@@ -26,8 +26,8 @@
 
 /**
  * A Zone is portion of the output page designed for easy dynamic updating via Ajax or other
client-side effects.  A
- * Zone renders out as a &lt;div&gt; element and may have content initially, or may
only get its content as a result of
- * client side activity.
+ * Zone renders out as a &lt;div&gt; element (or whatever is specified in the template)
and may have content initially,
+ * or may only get its content as a result of client side activity.
  * <p/>
  * Often, Zones are initially invisible, in which case the visible parameter may be set to
false (it defaults to true).
  * <p/>
@@ -66,6 +66,13 @@
     private String update;
 
     /**
+     * The element name to render for the zone; this defaults to the element actually used
in the template, or "div" if
+     * no specific element was specified.
+     */
+    @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.LITERAL)
+    private String elementName;
+
+    /**
      * If bound, then the id attribute of the rendered element will be this exact value.
If not bound, then a unique id
      * is generated for the element.
      */
@@ -88,12 +95,17 @@
     @Inject
     private ComponentResources resources;
 
+    String defaultElementName()
+    {
+        return resources.getElementName("div");
+    }
+
     void beginRender(MarkupWriter writer)
     {
         if (!resources.isBound("id"))
             clientId = renderSupport.allocateClientId(resources);
 
-        Element e = writer.element("div", "id", clientId);
+        Element e = writer.element(elementName, "id", clientId);
 
         resources.renderInformalParameters(writer);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueue.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueue.java?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueue.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderQueue.java
Fri Mar  6 23:43:41 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -83,6 +83,8 @@
      * service which are permanent, shared and stateless.
      * <p/>
      * Filters are added to the <em>end</em> of the pipeline (after all permanent
contributions).
+     * <p/>
+     * Filters will be executed in the order in which they are added.
      *
      * @param filter to add to the pipeline
      */

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
Fri Mar  6 23:43:41 2009
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.services;
 
 import org.apache.tapestry5.*;
+import org.apache.tapestry5.ajax.MultiZoneUpdate;
 import org.apache.tapestry5.annotations.*;
 import org.apache.tapestry5.beaneditor.Validate;
 import org.apache.tapestry5.corelib.data.BlankOption;
@@ -49,6 +50,8 @@
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.ComponentResourcesAware;
 import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+import org.apache.tapestry5.services.ajax.MultiZoneUpdateEventResultProcessor;
 import org.apache.tapestry5.util.StringToEnumCoercion;
 import org.apache.tapestry5.validator.*;
 import org.slf4j.Logger;
@@ -818,7 +821,7 @@
      * org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.Block} <li>String
to {@link java.text.DateFormat}
      * <li>{@link org.apache.tapestry5.PrimaryKeyEncoder} to {@link org.apache.tapestry5.ValueEncoder}
<li>String to
      * {@link org.apache.tapestry5.ioc.Resource} (via {@link org.apache.tapestry5.services.AssetSource#resourceForPath(String)})
-     * </ul>
+     * <li>{@link org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.runtime.RenderCommand}</li>
</ul>
      */
     public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
 
@@ -939,6 +942,20 @@
             }
         });
 
+        add(configuration, Renderable.class, RenderCommand.class, new Coercion<Renderable,
RenderCommand>()
+        {
+            public RenderCommand coerce(final Renderable input)
+            {
+                return new RenderCommand()
+                {
+                    public void render(MarkupWriter writer, RenderQueue queue)
+                    {
+                        input.render(writer);
+                    }
+                };
+            }
+        });
+
         add(configuration, PrimaryKeyEncoder.class, ValueEncoder.class, new PrimaryKeyEncoder2ValueEncoder(coercer));
     }
 
@@ -1473,9 +1490,9 @@
      * response</dd> <dt>String</dt> <dd>Interprets the value as
a logical page name and sends a client response to
      * redirect to that page</dd> <dt>{@link org.apache.tapestry5.Link}</dt>
<dd>Sends a JSON response to redirect to
      * the link</dd> <dt>{@link Class}</dt> <dd>Treats the class
as a page class and sends a redirect for a page render
-     * for that page</dd> </dl>
+     * for that page</dd> <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
<dd>Sends a single JSON response to
+     * update the content of multiple zones</dl>
      */
-
     public static void contributeAjaxComponentEventResultProcessor(
             MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
     {
@@ -1487,6 +1504,7 @@
         configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
         configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
         configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
+        configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
     }
 
     /**

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/CombinedRenderCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/CombinedRenderCommand.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/CombinedRenderCommand.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/CombinedRenderCommand.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,44 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.services.ajax;
+
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+
+/**
+ * Combines exactly two render commands by pushing each onto the render queue.
+ *
+ * @since 5.1.0.1
+ */
+public class CombinedRenderCommand implements RenderCommand
+{
+    private final RenderCommand first;
+
+    private final RenderCommand second;
+
+    public CombinedRenderCommand(RenderCommand first, RenderCommand second)
+    {
+        this.first = first;
+        this.second = second;
+    }
+
+    public void render(MarkupWriter writer, RenderQueue queue)
+    {
+        queue.push(second);
+
+        queue.push(first);
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/MultiZoneUpdateEventResultProcessor.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,89 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.services.ajax;
+
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.ajax.MultiZoneUpdate;
+import org.apache.tapestry5.internal.services.PageRenderQueue;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+import org.apache.tapestry5.services.ComponentEventResultProcessor;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Handler for {@link org.apache.tapestry5.ajax.MultiZoneUpdate} responses from a component
event handler method. Works
+ * by adding {@link org.apache.tapestry5.services.ajax.SingleZonePartialRendererFilter}s
for each zone to the
+ * {@linkplain org.apache.tapestry5.internal.services.PageRenderQueue#addPartialMarkupRendererFilter(org.apache.tapestry5.services.PartialMarkupRendererFilter)
+ * filter stack}.  Each zone writes its content as a string in the zones object of the reply,
keyed on its id.
+ * JavaScript and CSS are collected for all zones rendered in the request (not for each individua
zone).  The final
+ * repsonse will have some combination of "script", "scripts", "stylesheets", "content" (which
is expected to be blank)
+ * and "zones".
+ *
+ * @since 5.1.0.1
+ */
+public class MultiZoneUpdateEventResultProcessor implements ComponentEventResultProcessor<MultiZoneUpdate>
+{
+    private final PageRenderQueue queue;
+
+    private final TypeCoercer typeCoercer;
+
+    public MultiZoneUpdateEventResultProcessor(PageRenderQueue queue, TypeCoercer typeCoercer)
+    {
+        this.queue = queue;
+        this.typeCoercer = typeCoercer;
+    }
+
+    public void processResultValue(final MultiZoneUpdate value) throws IOException
+    {
+        // There has to be at least a single command in the queue to force a render.
+        queue.initializeForPartialPageRender(new RenderCommand()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+            }
+        });
+
+        queue.addPartialMarkupRendererFilter(new SetupZonesFilter());
+
+        Map<String, Object> map = value.getZoneToRenderMap();
+
+        for (String zoneId : map.keySet())
+        {
+            Object provided = map.get(zoneId);
+
+            RenderCommand zoneRenderCommand = toRenderer(zoneId, provided);
+
+            queue.addPartialMarkupRendererFilter(new SingleZonePartialRendererFilter(zoneId,
zoneRenderCommand, queue));
+        }
+    }
+
+    private RenderCommand toRenderer(String zoneId, Object provided)
+    {
+        try
+        {
+            return typeCoercer.coerce(provided, RenderCommand.class);
+        }
+        catch (Exception ex)
+        {
+            throw new IllegalArgumentException(String.format("Failure converting renderer
for zone '%s': %s",
+                                                             zoneId,
+                                                             InternalUtils.toMessage(ex)),
ex);
+        }
+    }
+}
\ No newline at end of file

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SetupZonesFilter.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,36 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.services.ajax;
+
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.services.PartialMarkupRenderer;
+import org.apache.tapestry5.services.PartialMarkupRendererFilter;
+
+/**
+ * Creates a "zones" object in the JSON reply, reading for the {@link org.apache.tapestry5.services.ajax.SingleZonePartialRendererFilter}s
+ * to store values into.
+ *
+ * @since 5.1.0.1
+ */
+public class SetupZonesFilter implements PartialMarkupRendererFilter
+{
+    public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer
renderer)
+    {
+        reply.put("zones", new JSONObject());
+
+        renderer.renderMarkup(writer, reply);
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/SingleZonePartialRendererFilter.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,85 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.services.ajax;
+
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.dom.Element;
+import org.apache.tapestry5.internal.services.PageRenderQueue;
+import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+import org.apache.tapestry5.services.PartialMarkupRenderer;
+import org.apache.tapestry5.services.PartialMarkupRendererFilter;
+
+/**
+ * Responsible for capturing the content for a single zone and storing it into the JSON reply
object.
+ *
+ * @see org.apache.tapestry5.ajax.MultiZoneUpdate
+ * @since 5.1.0.1
+ */
+public class SingleZonePartialRendererFilter implements PartialMarkupRendererFilter
+{
+    private final String zoneId;
+
+    private final RenderCommand zoneRenderCommand;
+
+    private final PageRenderQueue queue;
+
+    public SingleZonePartialRendererFilter(String zoneId, RenderCommand zoneRenderCommand,
PageRenderQueue queue)
+    {
+        this.zoneId = zoneId;
+        this.zoneRenderCommand = zoneRenderCommand;
+        this.queue = queue;
+    }
+
+    public void renderMarkup(MarkupWriter writer, final JSONObject reply, PartialMarkupRenderer
renderer)
+    {
+        RenderCommand forZone = new RenderCommand()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                // Create an element to contain the content for the zone. We give it a menumonic
+                // element name and attribute just to help with debugging (the element itself
is discarded).
+
+                final Element zoneContainer = writer.element("zone-update", "zoneId", zoneId);
+
+                queue.push(new RenderCommand()
+                {
+                    public void render(MarkupWriter writer, RenderQueue queue)
+                    {
+                        writer.end(); // the zoneContainer element
+
+                        String zoneUpdateContent = zoneContainer.getChildMarkup();
+
+                        zoneContainer.remove();
+
+                        reply.getJSONObject("zones").put(zoneId, zoneUpdateContent);
+                    }
+                });
+
+                // Make sure the zone's actual rendering command is processed first, then
the inline
+                // RenderCommand just above.
+
+                queue.push(zoneRenderCommand);
+            }
+        };
+
+        RenderCommand existing = queue.getRootRenderCommand();
+
+        queue.initializeForPartialPageRender(new CombinedRenderCommand(existing, forZone));
+
+        renderer.renderMarkup(writer, reply);
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/package.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/package.html?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/package.html
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ajax/package.html
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,3 @@
+<body>
+Utilities for handling Ajax-oriented requests, including partial render requests.
+</body>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
Fri Mar  6 23:43:41 2009
@@ -413,17 +413,30 @@
      * (an <a> or <form>) configured to update a zone. Writes errors to the AjaxConsole
      * if the zone and ZoneManager can not be resolved.
      *
-     * @param element   triggering element
+     * @param element   triggering element (id or instance)
      * @return Tapestry.ZoneManager instance for updated zone, or null if not found.
      */
     findZoneManager : function(element)
     {
         var zoneId = $T(element).zoneId;
-        var zoneElement = $(zoneId);
+
+        return Tapestry.findZoneManagerForZone(zoneId);
+    },
+
+    /**
+     * Obtains the Tapestry.ZoneManager object associated with a zone element (usually
+     * a <div>). Writes errors to the Ajax console if the element or manager
+     * can not be resolved.
+     * @param zoneElement  zone element (id or instance)
+     * @return Tapestry.ZoneManager instance for zone, or null if not found
+     */
+    findZoneManagerForZone : function(zoneElement)
+    {
+        var element = $(zoneElement);
 
         if (!zoneElement)
         {
-            Tapestry.ajaxError("Unable to locate Ajax Zone '#{id}' for dynamic update.",
{ id:zoneId});
+            Tapestry.ajaxError("Unable to locate Ajax Zone '#{id}' for dynamic update.",
{ id:zoneElement});
             return null;
         }
 
@@ -431,14 +444,13 @@
 
         if (!manager)
         {
-            Tapestry.ajaxError("Ajax Zone '#{id}' does not have an associated Tapestry.ZoneManager
object.", { id :zoneId });
+            Tapestry.ajaxError("Ajax Zone '#{id}' does not have an associated Tapestry.ZoneManager
object.", element);
             return null;
         }
 
         return manager;
     },
 
-
     /**
      * Used to reconstruct a complete URL from a path that is (or may be) relative to window.location.
      * This is used when determining if a JavaScript library or CSS stylesheet has already
been loaded.
@@ -455,7 +467,8 @@
             return path;
         }
 
-        if (! path.startsWith("/")) {
+        if (! path.startsWith("/"))
+        {
             Tapestry.error("External path " + path + " does not start with a leading slash.");
 
             return path;
@@ -1433,7 +1446,23 @@
     {
         Tapestry.loadScriptsInReply(reply, function()
         {
-            this.show(reply.content);
+            // In a multi-zone update, the reply.content may be blank or missing.
+
+            reply.content && this.show(reply.content);
+
+            // zones is an object of zone ids and zone content that will be present
+            // in a multi-zone update response.
+
+            Object.keys(reply.zones).each(function (zoneId)
+            {
+                var manager = Tapestry.findZoneManagerForZone(zoneId);
+
+                if (manager)
+                {
+                    var zoneContent = reply.zones[zoneId];
+                    manager.show(zoneContent);
+                }
+            });
         }.bind(this));
     },
 
@@ -1700,6 +1729,8 @@
 
                 if (Tapestry.ScriptManager.contains(document.scripts, "src", assetURL)) return;
// continue to next script
 
+                // IE needs the type="text/javascript" as well.
+
                 var element = new Element('script', { src: assetURL, type: 'text/javascript'
});
 
                 head.insert({bottom:element});

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MultiZoneUpdateDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MultiZoneUpdateDemo.tml?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MultiZoneUpdateDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MultiZoneUpdateDemo.tml Fri Mar 
6 23:43:41 2009
@@ -0,0 +1,51 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
+          xmlns:p="tapestry:parameter">
+
+    <h1>Multi-Zone Update Demo</h1>
+
+
+    <span id="now">${now}</span>
+
+    <div class="t-data-grid" style="{width: 100%;}">
+
+        <table class="t-data-grid">
+            <thead>
+
+                <tr>
+                    <th>Fred</th>
+                    <th>Barney</th>
+                    <th>Dino</th>
+                </tr>
+            </thead>
+            <tbody>
+
+                <tr>
+                    <td t:type="zone" t:id="fredZone" id="fred">
+                        Placeholder
+                    </td>
+                    <td t:type="zone" t:id="barneyZone" id="barney">
+                        Placeholder
+                    </td>
+                    <td t:type="zone" t:id="dinoZone" id="dino">
+                        Placeholder
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+
+    <t:actionlink t:id="update" zone="fred">update</t:actionlink>
+
+    <t:block id="fredBlock">
+        <span id="fredName">Fred Flintstone</span>
+    </t:block>
+
+    <t:block id="barneyBlock">
+        <t:form>
+            <t:palette t:id="options" model="options" encoder="encoder"/>
+            <input type="submit"/>
+
+        </t:form>
+    </t:block>
+
+</t:border>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
Fri Mar  6 23:43:41 2009
@@ -2787,4 +2787,28 @@
         // Make sure it was a partial update
         assertText("now", now);
     }
+
+    /**
+     * TAP5-108
+     */
+    public void update_multiple_zones_at_once()
+    {
+        start("Multiple Zone Update Demo");
+
+        String now = getText("now");
+
+
+        click("update");
+
+        waitForElementToAppear("fredName");
+
+        assertText("fredName", "Fred Flintstone");
+        assertText("dino", "His dog, Dino.");
+
+        // Ideally, we'd add checks that the JavaScript for the Palette in the Barney Zone
was
+        // updated.
+
+        // Make sure it was a partial update
+        assertText("now", now);
+    }
 }
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=751140&r1=751139&r2=751140&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
Fri Mar  6 23:43:41 2009
@@ -65,6 +65,9 @@
 
     private static final List<Item> ITEMS = CollectionFactory.newList(
 
+            new Item("MultiZoneUpdateDemo", "Multiple Zone Update Demo",
+                     "A single request can now update multiple Zones"),
+
             new Item("LinkSubmitInZoneDemo", "LinkSubmit inside Zone",
                      "Ensure that a LinkSubmit works correctly when its containing Form updates
a Zone"),
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateDemo.java?rev=751140&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateDemo.java
(added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/MultiZoneUpdateDemo.java
Fri Mar  6 23:43:41 2009
@@ -0,0 +1,49 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.ajax.MultiZoneUpdate;
+import org.apache.tapestry5.internal.services.StringValueEncoder;
+import org.apache.tapestry5.ioc.annotations.Inject;
+
+import java.util.Date;
+
+public class MultiZoneUpdateDemo
+{
+    @Inject
+    private Block fredBlock, barneyBlock;
+
+    public Date getNow()
+    {
+        return new Date();
+    }
+
+    Object onActionFromUpdate()
+    {
+        return new MultiZoneUpdate("fred", fredBlock).add("barney", barneyBlock).add("dino",
"His dog, Dino.");
+    }
+
+    public String[] getOptions()
+    {
+        return new String[] { "Red", "Green", "Blue" };
+    }
+
+    public ValueEncoder getEncoder()
+    {
+        return new StringValueEncoder();
+    }
+}



Mime
View raw message