From 151a3219a2c4f75fee055a2a424bd126f1109b48 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Sun, 1 Dec 2024 18:20:31 -0500 Subject: [PATCH] feature #551 - replace actionSubmit with formActionSubmit (#552) --- .../web/taglib/ApplicationTagLib.groovy | 18 ++-- .../plugins/web/taglib/FormTagLib.groovy | 81 ++++++++++++++++-- .../grails/web/taglib/FormTagLibTests.groovy | 82 ++++++++++++++++++- src/main/docs/guide/tags/formsAndFields.adoc | 4 +- src/main/docs/ref/Tags/actionSubmit.adoc | 2 + src/main/docs/ref/Tags/createLink.adoc | 8 +- src/main/docs/ref/Tags/form.adoc | 2 +- src/main/docs/ref/Tags/formActionSubmit.adoc | 63 ++++++++++++++ 8 files changed, 237 insertions(+), 23 deletions(-) create mode 100644 src/main/docs/ref/Tags/formActionSubmit.adoc diff --git a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/ApplicationTagLib.groovy b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/ApplicationTagLib.groovy index af6b0a7f41..920e9125b7 100644 --- a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/ApplicationTagLib.groovy +++ b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/ApplicationTagLib.groovy @@ -196,16 +196,20 @@ class ApplicationTagLib implements ApplicationContextAware, InitializingBean, Gr * <g:link action="myaction">link 1</gr:link>
* <g:link controller="myctrl" action="myaction">link 2</gr:link>
* - * @attr controller The name of the controller to use in the link, if not specified the current controller will be linked * @attr action The name of the action to use in the link, if not specified the default action will be linked - * @attr uri relative URI - * @attr url A map containing the action,controller,id etc. - * @attr base Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property, if both are specified. - * @attr absolute If set to "true" will prefix the link target address with the value of the grails.serverURL property from Config, or http://localhost:<port> if no value in Config and not running in production. + * @attr controller The name of the controller to use in the link, if not specified the current controller will be linked + * @attr namespace The namespace of the controller to use in the link + * @attr plugin The name of the plugin which provides the controller * @attr id The id to use in the link * @attr fragment The link fragment (often called anchor tag) to use - * @attr params A map containing URL query parameters * @attr mapping The named URL mapping to use to rewrite the link + * @attr method The HTTP method specified in the corresponding URL mapping + * @attr params A map containing URL query parameters for the link + * @attr url A map containing the action, controller, id etc. + * @attr uri A string for a relative path in the running app. + * @attr relativeUri Used to specify a uri relative to the current path. + * @attr absolute If set to "true" will prefix the link target address with the value of the grails.serverURL property from Config, or http://localhost:<port> if no value in Config and not running in production. + * @attr base Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property, if both are specified. * @attr event Webflow _eventId parameter * @attr elementId DOM element id */ @@ -345,7 +349,7 @@ class ApplicationTagLib implements ApplicationContextAware, InitializingBean, Gr * @attr controller The name of the controller to use in the link, if not specified the current controller will be linked * @attr action The name of the action to use in the link, if not specified the default action will be linked * @attr uri relative URI - * @attr url A map containing the action,controller,id etc. + * @attr url A map containing the action, controller, id etc. * @attr base Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property, if both are specified. * @attr absolute If set to "true" will prefix the link target address with the value of the grails.serverURL property from Config, or http://localhost:<port> if no value in Config and not running in production. * @attr id The id to use in the link diff --git a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy index aaf3089e3d..b2f6b2c659 100644 --- a/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy +++ b/grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy @@ -19,6 +19,7 @@ import grails.artefact.TagLibrary import grails.config.Config import grails.core.support.GrailsConfigurationAware import grails.gsp.TagLib +import grails.web.mapping.LinkGenerator import groovy.transform.CompileStatic import groovy.util.logging.Commons import org.grails.plugins.web.GrailsTagDateHelper @@ -30,7 +31,6 @@ import java.text.DateFormatSymbols import org.grails.core.artefact.DomainClassArtefactHandler import org.grails.encoder.CodecLookup import org.grails.encoder.Encoder -import grails.web.mapping.LinkGenerator import org.grails.buffer.FastStringWriter import org.grails.web.servlet.mvc.SynchronizerTokensHolder import org.grails.buffer.GrailsPrintWriter @@ -384,7 +384,7 @@ class FormTagLib implements ApplicationContextAware, InitializingBean, TagLibrar * @attr action the name of the action to use in the link, if not specified the default action will be linked * @attr controller the name of the controller to use in the link, if not specified the current controller will be linked * @attr id The id to use in the link - * @attr url A map containing the action,controller,id etc. + * @attr url A map containing the action, controller, id etc. * @attr name A value to use for both the name and id attribute of the form tag * @attr useToken Set whether to send a token in the request to handle duplicate form submissions. See Handling Duplicate Form Submissions * @attr method the form method to use, either 'POST' or 'GET'; defaults to 'POST' @@ -403,7 +403,7 @@ class FormTagLib implements ApplicationContextAware, InitializingBean, TagLibrar * @attr action the name of the action to use in the link, if not specified the default action will be linked * @attr controller the name of the controller to use in the link, if not specified the current controller will be linked * @attr id The id to use in the link - * @attr url A map containing the action,controller,id etc. + * @attr url A map containing the action, controller, id etc. * @attr name A value to use for both the name and id attribute of the form tag * @attr useToken Set whether to send a token in the request to handle duplicate form submissions. See Handling Duplicate Form Submissions * @attr method the form method to use, either 'POST' or 'GET'; defaults to 'POST' @@ -420,16 +420,16 @@ class FormTagLib implements ApplicationContextAware, InitializingBean, TagLibrar def linkAttrs = attrs.subMap(LinkGenerator.LINK_ATTRIBUTES) - writer << "
if (!attrs.value) { - throwTagError("Tag [actionSubmit] is missing required attribute [value]") + throwTagError('Tag [actionSubmit] is missing required attribute [value]') } attrs.tagName = "actionSubmit" @@ -548,6 +551,70 @@ class FormTagLib implements ApplicationContextAware, InitializingBean, TagLibrar out << '/>' } + /** + * Creates a submit button using the `formaction` attribute to submit to a different action than the form. + * The action will be generated by the various link attributes.
+ * + * <g:formActionSubmit action="myaction" value="Submit"/>
+ * <g:formActionSubmit controller="myctrl" action="myaction" value="ButtonName"/>
+ * + * @attr id the id attribute of the formActionSubmit tag + * @attr value the button's show value + * @attr action The name of the action to use in the link, if not specified the default action will be linked + * @attr controller The name of the controller to use in the link, if not specified the current controller will be linked + * @attr namespace The namespace of the controller to use in the link + * @attr plugin The name of the plugin which provides the controller + * @attr id The id to use in the link + * @attr fragment The link fragment (often called anchor tag) to use + * @attr mapping The named URL mapping to use to rewrite the link + * @attr method The HTTP method specified in the corresponding URL mapping + * @attr params A map containing URL query parameters for the link + * @attr url A map containing the action, controller, id etc. + * @attr uri A string for a relative path in the running app. + * @attr relativeUri Used to specify a uri relative to the current path. + * @attr absolute If set to "true" will prefix the link target address with the value of the grails.serverURL property from Config, or http://localhost:<port> if no value in Config and not running in production. + * @attr base Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the absolute property, if both are specified. + * @attr event Webflow _eventId parameter + */ + def formActionSubmit = { Map attrs -> + if (!attrs.value) { + throwTagError('Tag [formActionSubmit] is missing required attribute [value]') + } + + def elementId = attrs.remove('id') + + // the following attributes are reserved because this tag must be of type `submit` and the `formaction` attr + // will be generated by the link attributes. + attrs.remove('type') + attrs.remove('formAction') + + out << '' + } + /** * Creates a an image submit button that submits to an action in the controller specified by the form action. * The name of the action attribute is translated into the action name, for example "Edit" becomes diff --git a/grails-plugin-gsp/src/test/groovy/org/grails/web/taglib/FormTagLibTests.groovy b/grails-plugin-gsp/src/test/groovy/org/grails/web/taglib/FormTagLibTests.groovy index 8ae3df8751..b798fecee9 100644 --- a/grails-plugin-gsp/src/test/groovy/org/grails/web/taglib/FormTagLibTests.groovy +++ b/grails-plugin-gsp/src/test/groovy/org/grails/web/taglib/FormTagLibTests.groovy @@ -285,7 +285,12 @@ class FormTagLibTests extends Specification implements TagLibUnitTest' @@ -294,12 +299,85 @@ class FormTagLibTests extends Specification implements TagLibUnitTest\n' } + def testFormActionSubmitWithController() { + when: + String output = tagLib.formActionSubmit([controller: 'con', id: 'formElementId', value: 'Submit']) + + then: + output == '' + } + + def testFormActionSubmitWithControllerAndAction() { + when: + String output = tagLib.formActionSubmit([controller: 'con', action: 'act', id: 'formElementId', value: 'Submit']) + + then: + output == '' + } + + def testFormActionSubmitWithURLAndNoParams() { + when: + unRegisterRequestDataValueProcessor() + String output = tagLib.formActionSubmit(new TreeMap([url: [controller: 'con', action:'action'], id: 'formElementId', value: 'Submit'])) + + then: + output == '' + } + + def testFormActionSubmitWithAURLAndRequestDataValueProcessor() { + when: + String output = tagLib.formActionSubmit( + new TreeMap([ + url: [ + controller: 'con', + action:'action', + params: [ + requestDataValueProcessorParamName: 'paramValue' + ] + ], + id: 'formElementId', + value: 'My Button' + ]) + ) + + then: + output == '' + } + + def testFormActionSubmitWithAURLAndWithoutRequestDataValueProcessor() { + given: + unRegisterRequestDataValueProcessor() + + when: + String output = tagLib.formActionSubmit( + new TreeMap([ + url: [ + controller: 'con', + action:'action', + params: [ + requestDataValueProcessorParamName: 'paramValue' + ] + ], + id: 'formElementId', + value: 'My Button' + ]) + ) + + then: + output == '' + } + def testActionSubmitWithoutAction() { when: diff --git a/src/main/docs/guide/tags/formsAndFields.adoc b/src/main/docs/guide/tags/formsAndFields.adoc index 55a47aa332..c87b3efe63 100644 --- a/src/main/docs/guide/tags/formsAndFields.adoc +++ b/src/main/docs/guide/tags/formsAndFields.adoc @@ -37,9 +37,9 @@ GSP also contains extended helper versions of the above tags such as xref:../ref ==== Multiple Submit Buttons -The age-old problem of dealing with multiple submit buttons is also handled elegantly with Grails using the xref:../ref/Tags/actionSubmit.adoc[actionSubmit] tag. It is just like a regular submit, but lets you specify an alternative action to submit to: +The age-old problem of dealing with multiple submit buttons is also handled elegantly with Grails using the xref:../ref/Tags/formActionSubmit.adoc[formActionSubmit] tag. It is just like a regular submit, but lets you specify an alternative controller & action to submit to: [source,xml] ---- - + ---- diff --git a/src/main/docs/ref/Tags/actionSubmit.adoc b/src/main/docs/ref/Tags/actionSubmit.adoc index b60392b910..d49c256c00 100644 --- a/src/main/docs/ref/Tags/actionSubmit.adoc +++ b/src/main/docs/ref/Tags/actionSubmit.adoc @@ -8,6 +8,8 @@ Generates a submit button that maps to a specific action, which lets you have multiple submit buttons in a single form. JavaScript event handlers can be added using the same parameter names as in HTML. +_Note: this tag is being replaced by the tag formActionSubmit_ + === Examples diff --git a/src/main/docs/ref/Tags/createLink.adoc b/src/main/docs/ref/Tags/createLink.adoc index 5eb9c0ff2d..0d56f5bf52 100644 --- a/src/main/docs/ref/Tags/createLink.adoc +++ b/src/main/docs/ref/Tags/createLink.adoc @@ -85,14 +85,14 @@ Attributes * `action` (optional) - The name of the action to use in the link; if not specified the default action will be linked * `controller` (optional) - The name of the controller to use in the link; if not specified the current controller will be linked -* `namespace` (optional) - the namespace of the controller to use in the link -* `plugin` (optional) - the name of the plugin which provides the controller +* `namespace` (optional) - The namespace of the controller to use in the link +* `plugin` (optional) - The name of the plugin which provides the controller * `id` (optional) - The id to use in the link * `fragment` (optional) - The link fragment (often called anchor tag) to use * `mapping` (optional) - The {grailsdocs}guide/theWebLayer.html#namedMappings[named URL mapping] to use to rewrite the link * `method` (optional) - The HTTP method specified in the corresponding URL mapping -* `params` (optional) - A Map of request parameters -* `url` (optional) - A Map containing the action,controller,id etc. +* `params` (optional) - A map containing URL query parameters for the link +* `url` (optional) - A Map containing the action, controller, id etc. * `uri` (optional) - A string for a relative path in the running app. * `relativeUri` (optional) - Used to specify a uri relative to the current path. * `absolute` (optional) - If `true` will prefix the link target address with the value of the `grails.serverURL` property from the application configuration, or http://localhost: if there is no setting in the config and not running in production. diff --git a/src/main/docs/ref/Tags/form.adoc b/src/main/docs/ref/Tags/form.adoc index d514faee92..972d103a25 100644 --- a/src/main/docs/ref/Tags/form.adoc +++ b/src/main/docs/ref/Tags/form.adoc @@ -61,7 +61,7 @@ Attributes * `fragment` (optional) - The link fragment (often called anchor tag) to use * `mapping` (optional) - The {grailsdocs}guide/theWebLayer.html#namedMappings[named URL mapping] to use to rewrite the link * `params` (optional) - A Map of request parameters -* `url` (optional) - A map containing the action,controller,id etc. +* `url` (optional) - A map containing the action, controller, id etc. * `relativeUri` (optional) - Used to specify a uri relative to the current path. * `absolute` (optional) - If `true` will prefix the link target address with the value of the `grails.serverURL` property from the application configuration, or http://localhost: if there is no setting in the config and not running in production. * `base` (optional) - Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the `absolute` property, if both are specified. diff --git a/src/main/docs/ref/Tags/formActionSubmit.adoc b/src/main/docs/ref/Tags/formActionSubmit.adoc new file mode 100644 index 0000000000..88727deaa4 --- /dev/null +++ b/src/main/docs/ref/Tags/formActionSubmit.adoc @@ -0,0 +1,63 @@ + +== formActionSubmit + + + +=== Purpose + + +Creates a submit button that sets the `formaction` attribute, which allows one to submit to multiple different actions across different controllers. The `formaction` URL will be generated by the various link attributes. + + +=== Examples + + +[,xml] +---- + + + + + + + + + + + + + + + + + + + +---- + + +=== Description + + +Attributes + +* `value` (required) - The caption of the button and name of action when not explicitly defined. +* `id` (optional) - The element id of the input element +* `action` (optional) - The name of the action to use in the link; if not specified the default action will be linked +* `controller` (optional) - The name of the controller to use in the link; if not specified the current controller will be linked +* `namespace` (optional) - The namespace of the controller to use in the link +* `plugin` (optional) - The name of the plugin which provides the controller +* `id` (optional) - The id to use in the link +* `fragment` (optional) - The link fragment (often called anchor tag) to use +* `mapping` (optional) - The {grailsdocs}guide/theWebLayer.html#namedMappings[named URL mapping] to use to rewrite the link +* `method` (optional) - The HTTP method specified in the corresponding URL mapping +* `params` (optional) - A map containing URL query parameters for the link +* `url` (optional) - A Map containing the action, controller, id etc. +* `uri` (optional) - A string for a relative path in the running app. +* `relativeUri` (optional) - Used to specify a uri relative to the current path. +* `absolute` (optional) - If `true` will prefix the link target address with the value of the `grails.serverURL` property from the application configuration, or http://localhost: if there is no setting in the config and not running in production. +* `base` (optional) - Sets the prefix to be added to the link target address, typically an absolute server URL. This overrides the behaviour of the `absolute` property if both are specified. +* `event` (optional) - The name of a Webflow event to trigger for the flow associated with the given `action`. Requires the Webflow plugin. + +When you use a normal submit button inside a form, it is the form itself that determines what URL the request is sent to, and therefore what action is executed. However, this tag overrides that behaviour and determines which action is executed by the specified attributes. This tag supports any controller & action.