3

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

使用 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,我怎么能做到这一点?(除了发布的参数之外,我可以在 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

4 回答 4

1

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

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

<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 时)。

于 2013-08-14T16:54:46.697 回答
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
}

或者,如果您更喜欢 groovier 版本

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
}
于 2012-05-25T05:44:36.337 回答
0

您只能使用一个提交按钮,并将g:eachradioGroup

例子:

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

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

于 2012-05-26T02:55:09.710 回答
0

我将尝试将参数添加到 actionSubmit 中的操作名称:

<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)
        }
    }
}
于 2012-06-08T00:28:14.083 回答