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 <hls...@apache.org> Authored: Wed Oct 3 12:07:21 2012 -0700 Committer: Howard M. Lewis Ship <hls...@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