You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/10/03 21:07:42 UTC

[1/10] git commit: Rebuild FormFragment client-side support

Updated Branches:
  refs/heads/5.4-js-rewrite 48bec3a6d -> 654f3ef1a


Rebuild FormFragment client-side support


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/654f3ef1
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/654f3ef1
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/654f3ef1

Branch: refs/heads/5.4-js-rewrite
Commit: 654f3ef1a58335ccadf1309acc7948a8f1863025
Parents: 423df6d
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Wed Oct 3 12:07:21 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed Oct 3 12:07:21 2012 -0700

----------------------------------------------------------------------
 .../META-INF/modules/core/events.coffee            |   13 +++
 .../META-INF/modules/core/form-fragment.coffee     |   62 ++++++++-------
 .../META-INF/modules/core/forms.coffee             |    6 +-
 .../tapestry5/corelib/components/FormFragment.java |   49 ++++++------
 .../resources/org/apache/tapestry5/tapestry.js     |    4 +-
 5 files changed, 75 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/654f3ef1/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
index 0131c15..2b649ea 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/events.coffee
@@ -29,6 +29,7 @@ define
     # Triggered after `validateForm` (when there are no prior validation exceptions), to allow certain elements
     # to configure themselves immediately before the form is submitted. This exists primarily for components such
     # as FormFragment, which will update a enable or disable a hidden field to match the visibility of the fragment.
+    # The `core/spi.EventWrapper` for the form element is passed as the memo.
     prepareForSubmit: "t5:form:prepare-for-submit"
 
     # Triggered last, when the form is configured to not submit normally (as a standard POST). Under 5.3, this
@@ -36,6 +37,7 @@ define
     # set the `data-prevent-submission` attribute. In either case, the submit event is stopped, and this
     # event fired to replace it; in most cases, a handler will then handle submitting the form's data as part
     # of an Ajax request.
+    # The `core/spi.EventWrapper` for the form element is passed as the memo.
     processSubmit: "t5:form:process-submit"
 
   field:
@@ -71,3 +73,14 @@ define
     didShow: "t5:element:did-show"
     # Trigered when a visible element has just been hidden.
     didHide: "t5:element:did-hide"
+  # Event names specific to client-side element associated with the FormFragment component. These events exist to allow
+  # client code to cleanly adjust the visibility of the fragment, or remove it.
+  formfragment:
+    # Requests that the fragment change its visibility. The event memo is an object with a single key, visible, a
+    # boolean. The fragment will show or hide itself if necessary (triggering the `element.didShow` or
+    # `element.didHide` event).
+    changeVisibility: "t5:fragment:change-visibility"
+    # Request that the fragment remove itself entirely. This event is of no practical use, as it is simply equivalent
+    # to invoking `spi/ElementWrapper.remove()` on the fragment's element; the event exists for compatibility with
+    # Tapestry 5.3 and will be removed in Tapestry 5.5.
+    remove: "t5:fragment:remove"

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/654f3ef1/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
index 601f0ac..699f3af 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/form-fragment.coffee
@@ -15,45 +15,49 @@
 
 # ##core/form-fragment
 #
-define ["core/spi", "core/events", "core/compat/tapestry"],
-  (spi, events) ->
+define ["_", "core/spi", "core/events", "core/compat/tapestry"],
+  (_, spi, events) ->
 
-    # Initializes a FormFragment element
-    #
-    # * spec.element - id of fragment's element
-    # * spec.bound - (optional) reference to function that determines bound
-    #   (used with `core/spi:EventWrapper.deepVisible()`)
-    # * spec.alwaysSubmit - (optional) if true, then fields inside the fragment submit their values
-    #   even when the fragment is not visible. If false (default), then field data is not submitted.
-    initFragment = (spec) ->
-      element = spi spec.element
-      hidden = spi "#{spec.element}-hidden"
-      form = spi hidden.element.form
+    SELECTOR = '[data-component-type="core/FormFragment"]'
+
+    # Setup up top-level event handlers for FormFragment-related DOM events.
+    spi.domReady ->
+      body = spi.body()
+
+      # This is mostly for compatibility with 5.3, which supported
+      # a DOM event to ask a fragment to remove itself.  This makes less sense since
+      # default animations were eliminated in 5.4.
+      body.on events.formfragment.remove, SELECTOR, (event) ->
+        this.remove()
 
-      opts = spec.bound and { bound: spec.bound} or null
+      # When any form fires the prepareForSubmit event, check to see if
+      # any form fragments are contained within, and give them a chance
+      # to enabled/disable their hidden field.
+      body.on events.form.prepareForSubmit, "form", (event) ->
 
-      unless spec.alwaysSubmit
-        hidden.element.disabled = ! element.deepVisible opts
+        fragments = this.findAll SELECTOR
 
-      updateUI = (makeVisible) ->
-        unless spec.alwaysSubmit
-          hidden.element.disabled = ! (makeVisible and element.container().deepVisible opts)
+        _.each fragments, (frag) ->
 
-        element[if makeVisible then "show" else "hide"]()
+          fragmentId = frag.getAttribute "id"
 
-        element.trigger events.element[if makeVisible then "didShow" else "didHide"]
+          hidden = frag.find "input[type=hidden][data-for-fragment=#{fragmentId}]"
 
-      element.on Tapestry.CHANGE_VISIBILITY_EVENT, (event) ->
+          # If found (e.g., not alwaysSubmit), then enable/disable the field.
+          hidden && hidden.setAttribute "disabled", not frag.deepVisible()
+
+      # Again, a DOM event to make the FormFragment visible or invisible; this is useful
+      # because of the didShow/didHide events ... but we're really just seeing the evolution
+      # from the old style (the FormFragment class as controller) to the new style (DOM events and
+      # top-level event handlers).
+      body.on events.formfragment.changeVisibility, SELECTOR, (event) ->
         event.stop()
 
         makeVisible = event.memo.visible
 
-        unless makeVisible is element.visible()
-          updateUI makeVisible
+        this[if makeVisible then "show" else "hide"]()
 
-      element.on Tapestry.HIDE_AND_REMOVE_EVENT, (event) ->
-        event.stop()
-        element.remove()
+        this.trigger events.element[if makeVisible then "didShow" else "didHide"]
 
     # Initializes a trigger for a FormFragment
     #
@@ -68,11 +72,11 @@ define ["core/spi", "core/events", "core/compat/tapestry"],
         checked = trigger.element.checked
         makeVisible = checked isnt invert
 
-        (spi spec.fragmentId).trigger Tapestry.CHANGE_VISIBILITY_EVENT,  visible: makeVisible
+        (spi spec.fragmentId).trigger events.formfragment.changeVisibility,  visible: makeVisible
 
       if trigger.element.type is "radio"
         spi.on trigger.element.form, "click", update
       else
         trigger.on "click", update
 
-    { initFragment, linkTrigger }
+    { linkTrigger }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/654f3ef1/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
index 05fe893..d9e424f 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/forms.coffee
@@ -69,14 +69,14 @@ define ["core/events", "core/spi", "core/builder", "core/compat/tapestry"],
       # Allow certain types of elements to do last-moment set up. Basically, this is for
       # FormFragment, or similar, to make their t:hidden field enabled or disabled to match
       # their UI's visible/hidden status. This is assumed to work.
-      this.trigger events.form.prepareForSubmit
+      this.trigger events.form.prepareForSubmit, this
 
       # Sometimes we want to submit the form normally, for a full-page render.
       # Othertimes we want to stop here and let the `events.form.processSubmit`
       # handler take it from here.
       if isPreventSubmission this
         event.stop()
-        this.trigger events.form.processSubmit
+        this.trigger events.form.processSubmit, this
 
       # Otherwise, the event is good, there are no validation problems, let the normal processing commence.
       return
@@ -89,7 +89,7 @@ define ["core/events", "core/spi", "core/builder", "core/compat/tapestry"],
       # On any click on a submit or image, update the containing form to indicate that the element
       # was responsible for the eventual submit; this is very important to Ajax updates, otherwise the
       # information about which control triggered the submit gets lost.
-      spi.body().on "click", "input[type=submit], input[type=image]", (event) ->
+      spi.body().on "click", "input[type=submit], input[type=image]", ->
         setSubmittingHidden (spi this.element.form), this
 
     exports =

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/654f3ef1/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
index 0e08011..4a22299f 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
@@ -16,6 +16,7 @@ package org.apache.tapestry5.corelib.components;
 
 import org.apache.tapestry5.*;
 import org.apache.tapestry5.annotations.Environmental;
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Parameter;
 import org.apache.tapestry5.annotations.SupportsInformalParameters;
 import org.apache.tapestry5.corelib.internal.ComponentActionSink;
@@ -24,8 +25,6 @@ import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner;
 import org.apache.tapestry5.corelib.mixins.TriggerFragment;
 import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.ioc.annotations.Inject;
-import org.apache.tapestry5.json.JSONLiteral;
-import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.services.ClientDataEncoder;
 import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.FormSupport;
@@ -38,16 +37,16 @@ import org.slf4j.Logger;
  * automatically bypass validation when the fragment is invisible. The trick is to also bypass server-side form
  * processing for such fields when the form is submitted; client-side logic "removes" the
  * {@link org.apache.tapestry5.corelib.components.Form#FORM_DATA form data} for the fragment if it is invisible when the
- * form
- * is submitted; alternately, client-side logic can simply remove the form fragment element (including its visible and
+ * form is submitted (e.g., the hidden form field is disabled);
+ * alternately, client-side logic can simply remove the form fragment element (including its visible and
  * hidden fields) to prevent server-side processing.
  * <p/>
- * The client-side element will now listen to two new event defined by client-side constants:
+ * The client-side element will now listen to two new events defined by client-side constants:
  * <dl>
- * <dt>Tapestry.CHANGE_VISIBILITY_EVENT</dt>
+ * <dt>core/events.formfragment.changeVisibility or Tapestry.CHANGE_VISIBILITY_EVENT</dt>
  * <dd>Change the visibility as per the event memo's visibility property. When the visibility changes, the correct
  * animation is executed.</dd>
- * <dt>Tapestry.HIDE_AND_REMOVE_EVENT</dt>
+ * <dt>core/events.formfragment.remove or Tapestry.HIDE_AND_REMOVE_EVENT</dt>
  * <dd>Hides the element, then removes it from the DOM entirely.
  * </dl>
  *
@@ -56,6 +55,7 @@ import org.slf4j.Logger;
  * @see Form
  */
 @SupportsInformalParameters
+@Import(modules = "core/form-fragment")
 public class FormFragment implements ClientElement
 {
     /**
@@ -109,13 +109,14 @@ public class FormFragment implements ClientElement
     private String idParameter;
 
     /**
-     * A javascript function that overrides the default visibility search bound.
+     * The name of a javascript function that overrides the default visibility search bound.
      * Tapestry normally ensures that not only the form fragment but all parent elements up to the containing body
      * are visible when determining whether to submit the contents of a form fragment.  This behavior can be modified by
      * supplying a javascript function that receives the "current" element in the chain.  Returning true will stop the
      * search (and report ElementWrapper.deepVisible() as true).  Returning false will continue the search up the chain.
      *
      * @since 5.3
+     * @deprecated Deprecated in 5.4 with no current replacement.
      */
     @Parameter(defaultPrefix = BindingConstants.LITERAL, allowNull = false)
     private String visibleBound;
@@ -161,7 +162,9 @@ public class FormFragment implements ClientElement
 
         hiddenFieldPositioner = new HiddenFieldPositioner(writer, rules);
 
-        Element element = writer.element(this.element, "id", clientId);
+        Element element = writer.element(this.element,
+                "id", clientId,
+                "data-component-type", "core/FormFragment");
 
         resources.renderInformalParameters(writer);
 
@@ -170,20 +173,6 @@ public class FormFragment implements ClientElement
             element.addClassName(CSSClassConstants.INVISIBLE);
         }
 
-        JSONObject spec = new JSONObject("element", clientId);
-
-        if (visibleBound != null)
-        {
-            spec.put("bound", new JSONLiteral(visibleBound));
-        }
-
-        if (alwaysSubmit)
-        {
-            spec.put("alwaysSubmit", true);
-        }
-
-        javascriptSupport.require("core/form-fragment").invoke("initFragment").with(spec);
-
         componentActions = new ComponentActionSink(logger, clientDataEncoder);
 
         // Here's the magic of environmentals ... we can create a wrapper around
@@ -223,16 +212,26 @@ public class FormFragment implements ClientElement
      */
     void afterRender(MarkupWriter writer)
     {
-        hiddenFieldPositioner.getElement().attributes("type", "hidden",
+        Element hidden = hiddenFieldPositioner.getElement();
+
+        hidden.attributes("type", "hidden",
 
                 "name", Form.FORM_DATA,
 
-                "id", clientId + "-hidden",
 
                 "value", componentActions.getClientData());
 
+        if (!alwaysSubmit)
+        {
+            // Make it possible for the FormFragment to locate the hidden field, even if
+            // FormFragments get nested in some complex way.  When the always submit option
+            // is enabled, there's no need for the hidden field to be locatable.
+            hidden.attributes("data-for-fragment", clientId);
+        }
+
         writer.end(); // div
 
+
         environment.pop(FormSupport.class);
     }
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/654f3ef1/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js b/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
index de4dd63..8af4064 100644
--- a/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
+++ b/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
@@ -79,13 +79,13 @@ define("core/compat/tapestry", [
          * fragment. The event memo object includes a key, visible, that should be
          * true or false.
          */
-        CHANGE_VISIBILITY_EVENT: "tapestry:changevisibility",
+        CHANGE_VISIBILITY_EVENT: events.formfragment.changeVisibility,
 
         /**
          * Event fired on a form fragment element to hide the element and remove it
          * from the DOM.
          */
-        HIDE_AND_REMOVE_EVENT: "tapestry:hideandremove",
+        HIDE_AND_REMOVE_EVENT: events.formfragment.remove,
 
         /**
          * Event fired on a link or submit to request that it request that the