如何在不使用JavaScript的情况下向提交按钮添加参数?

时间:2012-05-25 03:32:58

标签: grails

我的表单有一个项目列表,每个项目都有一个删除按钮。我需要提交要删除的项目的索引以及表单的其他值(以便进一步编辑)。

使用JavaScript,它看起来像这样:

<g:form method="post" mapping="defaultAction" id="${paymentInstance?.id}">
    <g:hiddenField name="id" value="${paymentInstance?.id}"/>
    <g:hiddenField name="version" value="${paymentInstance?.version}"/>
    <g:hiddenField name="deleteAdjIdx"/>
    <g:each in="${paymentInstance?.adjustments}" var="adj" status="idx">
        <g:set var="adjName" value="adjustments[${idx}]"/>
        <g:textField name="${adjName}.dollars" value="${adj.dollars}"/>
        <g:actionSubmit action="deleteAdj" value="delete" onclick="jQuery('#deleteAdjIdx').val(${idx})"/>
    </g:each>
</g:form>

如果没有JavaScript,我怎么能这样做? (除了发布的params之外,我可以在URL中添加一个参数吗?或者,映射中的某种多路复用?)

defaultAction映射是:

    name defaultAction: "/$controller/$id"{     // stable URL for payments regardless of current status (editable or not)
        constraints {
            id(matches: /\d+/)      // since our action names don't start with a digit but many domain ids do
        }
    }

4 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,并找到了使用HTML5的“格式”属性的另一种解决方案。 它们可以被赋予一个值,包括控制器,动作,附加参数等。

通常,在表单上向提交按钮添加参数(例如删除特定的子对象)将如下所示:

<input type="submit" formaction="/<controller>/<action>/<id>?additionalParam1=...&additionalParam2=..." value="Delete" >

并在您的示例中:

<input type="submit" formaction="/payment/deleteAdj/${adj.id}" value="delete" >

<input type="submit" formaction="/payment/deleteAdj?delAdjID=${adj.id}" value="delete" >

在deleteAdj中你会删除params.id或params.delAdjID中所述的调整并保存 params中的数据或直接使用params重新填充表单(例如,在渲染create.gsp时)。

答案 1 :(得分:0)

您的代码似乎创建了多个删除按钮,每个按钮点击重新加载页面。

那么为什么不创建几种形式:

<g:each in="${paymentInstance?.adjustments}" var="adj" status="idx">
    <g:form method="post" mapping="defaultAction" id="${paymentInstance?.id}">
        <g:hiddenField name="id" value="${paymentInstance?.id}"/>
        <g:hiddenField name="version" value="${paymentInstance?.version}"/>
        <g:hiddenField name="deleteAdjIdx" value="${idx}"/>
        <g:set var="adjName" value="adjustments[${idx}]"/>
        <g:textField name="${adjName}.dollars" value="${adj.dollars}"/>
        <g:actionSubmit action="deleteAdj" value="delete" />
    </g:form>
</g:each>

<强>更新

如果您需要一个表单,您还可以使用常规提交按钮:为表单提供固定操作,并将提交按钮设为值:

<g:form method="post" 
        controller="payment" action="myAction" 
        id="${paymentInstance?.id}">
    <g:each in="${paymentInstance?.adjustments}" var="adj" status="idx">
        <g:hiddenField name="id" value="${paymentInstance?.id}"/>
        <g:hiddenField name="version" value="${paymentInstance?.version}"/>
        <g:set var="adjName" value="adjustments[${idx}]"/>
        <g:textField name="${adjName}.dollars" value="${adj.dollars}"/>
        <input type="submit" name="delete" value="${idx}"/>
    </g:each>
</g:form>

这应该会产生一个由payment控制器和myAction处理的表单。在此操作中,您可以检查params.delete是否存在。此参数的值将是${idx}的值。

如果您需要此表单的其他操作,您只需以相同的方式添加其他提交按钮,并检查其params-value是否存在。然后,您可以跳转到控制器中的另一个操作(这就是在后台处理这些<g:actionSubmit .../>按钮的方式。

更新2: 你是绝对正确的:按钮上显示的索引是丑陋的。另一种方法是使用我第一次更新的代码并将按钮更改为

        <input type="submit" name="delete_${idx}" value="Delete me!"/>

在你的控制器中,你必须使用这样的代码

def idx = null
params.each { key, value ->
  if (key.startsWith('delete_')) {
    idx = key-"delete_"
  }
}
if (idx) {
  // delete element
}

或者如果你喜欢一个更酷的版本

def idx = (params.find { key, value -> key.startsWith('delete_')})?.key
if (idx) {
  id -= 'delete_'
  // delete element
}

甚至更混淆: - )

def idx = ''+(params.find { key, value -> key.startsWith('delete_')})?.key-'null'-'delete_'
if (idx) {
  // delete element
}

答案 2 :(得分:0)

您只能使用一个提交按钮,并将g:each替换为radioGroup

示例:

<g:radioGroup name="adj" labels="[put here your text]" values="[put here the indexes]">
   <p>${it.label} ${it.radio}</p>
</g:radioGroup>

现在adj将保留要删除的项目的值

答案 3 :(得分:0)

我将尝试在actionSubmit中添加params到动作名称:

<my:actionSubmit action="deleteAdj" value="delete" params="${[deleteAdjIdx: idx]})"/>

我没有找到一个很好的方法来做到这一点,所以我提出了以下黑客攻击。如果有人知道任何改进,我会很感激。

添加到MyTagLib:

import org.codehaus.groovy.grails.web.util.WebUtils

def actionSubmit = { attrs ->
    String action = attrs.action ?: attrs.value
    Map params = attrs.remove('params')
    if (params) {
        attrs.action = (action + WebUtils.toQueryString(params)).encodeAsURL()
    }
    out << g.actionSubmit(attrs)
}

我解压缩参数并在过滤器中恢复原始操作名称:

package com.example;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.web.filter.GenericFilterBean
import org.codehaus.groovy.grails.web.util.WebUtils
import com.example.util.MyWebUtils
import javax.servlet.http.HttpServletRequest;

/**
 * Handles params packed into the MyTagLib.actionSubmit param name.
 * This Filter needs to come before GrailsWebRequestFilter.
 * <p/>
 * Updating the GrailsParameterMap after the GrailsWebRequestFilter doesn't work,
 * because the request given to DefaultUrlMappingInfo.checkDispatchAction()
 * is the immutable request saved in the GrailsWebRequest, not the wrapped request
 * that this filter passes on down the chain.
 */
public class ActionSubmitParamFilter extends GenericFilterBean {

    final static QUERY_SEPARATOR = '?'
    final static QUERY_SEPARATOR_REGEX = '[?]'
    final static PARAM_SEPARATOR_REGEX = '[&]'

    @Override
    void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        def dispatchAction = request.parameterNames.find {it.startsWith(WebUtils.DISPATCH_ACTION_PARAMETER)}
        def decoded = dispatchAction?.decodeURL()
        if (decoded?.contains(QUERY_SEPARATOR)) {
            def overrides = unpackParams(decoded)
            overrides[dispatchAction] = null   // deletes from the overridden request params
            request = MyWebUtils.overrideParams((HttpServletRequest) request, overrides)
        }
        chain.doFilter(request, response);
    }

    private Map unpackParams(String decoded) {
        String action, paramsPart
        (action, paramsPart) = decoded.split(QUERY_SEPARATOR_REGEX)
        assert action.startsWith(WebUtils.DISPATCH_ACTION_PARAMETER)
        def packedParams = [:].withDefault {[]}
        packedParams[action] << ''

        for (param in paramsPart.split(PARAM_SEPARATOR_REGEX)) {
            if (param.contains('=')) {
                def (name, value) = param.split('=')
                packedParams[name.decodeURL()] << value.decodeURL()
            } else {
                packedParams[param.decodeURL()] << ''
            }
        }
        packedParams
    }
}

我使用实用程序方法来覆盖请求参数并配置过滤器:

package com.example.util

import org.springframework.web.multipart.MultipartHttpServletRequest
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletRequestWrapper
import groovy.util.slurpersupport.GPathResult
import com.example.ActionSubmitParamFilter

class MyWebUtils {

    static HttpServletRequest overrideParams(HttpServletRequest original, Map<String, List<String>> overrides) {
        assert !(original instanceof MultipartHttpServletRequest)   // if we get one of these, we'll need to use a special wrapper

        def params = (Map<String, String[]>) new HashMap(original.getParameterMap())
        overrides.each {k, v ->
            if (v == null) {
                params.remove(k)    // significant for parameterNames enumeration and parameterMap
            } else {
                params[k] = v as String[]
            }
        }
        params = Collections.unmodifiableMap(params)
        new HttpServletRequestWrapper(original) {

            @Override
            String[] getParameterValues(String name) {
                params[name]
            }

            @Override
            String getParameter(String name) {
                String[] values = params[name]
                values ? values[0] : (values == null ? null : '')
            }

            @Override
            Map getParameterMap() {
                params
            }

            @Override
            Enumeration getParameterNames() {
                Collections.enumeration(params.keySet())
            }
        }
    }


    // used dynamically by _Events.eventWebXmlStart
    static void prependActionSubmitParamFilter(GPathResult webXml) {

        def filters = webXml.filter
        def filterMappings = webXml.'filter-mapping'

        def firstFilter = filters[0]
        def firstFilterMapping = filterMappings[0]

        firstFilter + {
            filter {
                'filter-name'('actionSubmitParam')
                'filter-class'(ActionSubmitParamFilter.name)
            }
        }

        firstFilterMapping + {
            'filter-mapping' {
                'filter-name'('actionSubmitParam')
                'url-pattern'("/*")
                'dispatcher'("FORWARD")
                'dispatcher'("REQUEST")
            }
        }
    }
}

最后,我在_Events.groovy中添加了以下内容,通过扩展ControllersGrailsPlugin.doWithWebDescriptor在web.xml中配置过滤器:

eventWebXmlStart = {

    pluginManager.getGrailsPlugin('controllers').with {
        def originalClosure = instance.doWithWebDescriptor  // extending
        instance.doWithWebDescriptor = { GPathResult webXml ->

            // static import fails after clean (before compile), so load dynamically
            def webUtils = getClass().classLoader.loadClass('com.example.util.MyWebUtils')
            webUtils.prependActionSubmitParamFilter(webXml)

            // call super after (so above filter comes before GrailsWebRequestFilter in chain)
            originalClosure.resolveStrategy = Closure.DELEGATE_FIRST
            originalClosure.delegate = delegate
            originalClosure(webXml)
        }
    }
}