如何处理ASP.NET MVC Framework中的多个提交按钮?


733

是否有一些简单的方法可以处理来自同一表单的多个提交按钮?例如:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>

任何想法如何在ASP.NET Framework Beta中执行此操作?我搜索过的所有示例中都包含一个按钮。


6
值得一提的是,ASP.NET Core开始,解决方案比这里列出的解决方案简单得多。
史蒂芬·杰里斯

Answers:


629

这主要是基于Maarten Balliauw的帖子和评论的,基于属性的多属性按钮解决方案。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
    public string Name { get; set; }
    public string Argument { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var keyValue = string.Format("{0}:{1}", Name, Argument);
        var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
            isValidName = true;
        }

        return isValidName;
    }
}

剃刀:

<form action="" method="post">
 <input type="submit" value="Save" name="action:Save" />
 <input type="submit" value="Cancel" name="action:Cancel" />
</form>

和控制器:

[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }

[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }

更新: 剃刀页面看起来可以提供相同的功能。对于新开发,它可能是更可取的。


3
我发现此解决方案是所使用的其他技术的美满婚姻。完美工作,不会影响本地化。
trevorc

17
很好的解决方案!!比那些得分最高的答案有用得多
Sasha 2012年

11
这种方法的问题在于,如果您尝试return View(viewmodel)在模型有错误的情况下尝试执行操作,则它将尝试返回名为Send或取决于参数名称的视图。
Shoe

15
@Shoe-刚刚发现了类似的东西。如果使用此方法,请确保明确指定要返回的视图的名称:return View("Index", viewModel)
ajbeaven 2013年

4
只是一种信息,我们需要添加system.Reflection for MethodInfo
Vignesh Subramanian

469

给您的提交按钮命名,然后在控制器方法中检查提交的值:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="Send" />
<input type="submit" name="submitButton" value="Cancel" />
<% Html.EndForm(); %>

发布到

public class MyController : Controller {
    public ActionResult MyAction(string submitButton) {
        switch(submitButton) {
            case "Send":
                // delegate sending to another controller action
                return(Send());
            case "Cancel":
                // call another action to perform the cancellation
                return(Cancel());
            default:
                // If they've submitted the form without a submitButton, 
                // just return the view again.
                return(View());
        }
    }

    private ActionResult Cancel() {
        // process the cancellation request here.
        return(View("Cancelled"));
    }

    private ActionResult Send() {
        // perform the actual send operation here.
        return(View("SendConfirmed"));
    }

}

编辑:

为了将这种方法扩展到与本地化站点一起使用,请将您的消息隔离在其他地方(例如,将资源文件编译为强类型的资源类)

然后修改代码,使其工作如下:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="<%= Html.Encode(Resources.Messages.Send)%>" />
<input type="submit" name="submitButton" value="<%=Html.Encode(Resources.Messages.Cancel)%>" />
<% Html.EndForm(); %>

并且您的控制器应如下所示:

// Note that the localized resources aren't constants, so 
// we can't use a switch statement.

if (submitButton == Resources.Messages.Send) { 
    // delegate sending to another controller action
    return(Send());

} else if (submitButton == Resources.Messages.Cancel) {
     // call another action to perform the cancellation
     return(Cancel());
}

28
太糟糕了,您取决于按钮上显示的文本,对于多语言用户界面来说,这有点棘手
Omu 2010年

3
开关/大小写仅适用于常量,因此本地化版本不能使用开关/大小写。您需要切换到if else或其他某种调度方法。
mlibby 2010年

10
您应该使用<button type =“ submit”而不是<input type,因为<button type的值不是text;)。然后,您可以看到类似以下内容的内容:<button name =“ mySubmitButton” type =“ submit” value =“ keyValue”> YourButtonText </ button>
J4N 2012年

4
将模型传递给操作而不只是提交值,这将如何工作?
bizzehdee

2
如果您使用的是jQuery,请注意不要将按钮命名为“ action”。这会导致库中发生冲突,从而导致操作URL中断。
HotN

121

您可以按照前面提到的操作检查名称,但是您可能会考虑这是否是一个好的设计。最好考虑一下操作的责任,并且不要将此设计与按钮名称之类的UI方面过多耦合。因此,请考虑使用2种形式和2种动作:

<% Html.BeginForm("Send", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<% Html.EndForm(); %>

<% Html.BeginForm("Cancel", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

另外,在“取消”的情况下,您通常只是不处理表单,而是转到新的URL。在这种情况下,您根本不需要提交表单,只需一个链接:

<%=Html.ActionLink("Cancel", "List", "MyController") %>

53
如果您不需要每个提交按钮都使用相同的表单数据,则可以。如果您需要所有通用格式的数据,那么Dylan Beattie就是您的最佳选择。还有其他更优雅的方法吗?
zidane

3
关于视觉呈现,在这种情况下,如何在“取消”按钮旁边设置“发送”按钮?
Kris-I

1
迪伦:对于取消按钮,您根本不需要提交数据,并且将控制器耦合到UI元素是一种不好的做法。但是,如果您可以创建一个或多或少的通用“命令”,那么我认为可以,但是我不会将其绑定到“ submitButton”,因为这是UI元素的名称。
2011年

1
@Kris:您可以使用CSS定位按钮,它们仍然可以位于2个不同的表单区域中。
Trevor de Koekkoek 2011年

8
认真吗 除了我,这对其他人没有异味吗?

98

Eilon建议您可以这样:

如果您有多个按钮,则可以通过为每个按钮命名来区分它们:

<input type="submit" name="SaveButton" value="Save data" />
<input type="submit" name="CancelButton" value="Cancel and go back to main page" />

在控制器操作方法中,可以添加以HTML输入标签名称命名的参数:

public ActionResult DoSomeStuff(string saveButton, string
cancelButton, ... other parameters ...)
{ ... }

如果将任何值发布到这些参数之一,则意味着该按钮是被单击的按钮。网络浏览器将仅发布一个按钮的值。所有其他值将为null。

if (saveButton != null) { /* do save logic */ }
if (cancelButton != null) { /* do cancel logic */ }

我喜欢这种方法,因为它不依赖于提交按钮的value属性,该属性比分配的名称更容易更改,并且不需要启用javascript

请参阅:http//forums.asp.net/p/1369617/2865166.aspx#2865166


2
如果有人遇到这个老问题,如果您不想使用HTML5 <button>元素,那么这是最干净的答案。如果您不介意HTML5,则将<button>与value属性一起使用。
Kugel 2013年

如果在此表单上创建ajax调用,您将如何处理。这似乎form.serialize()不拾了提交按钮的名称..
人民圣战者组织

@Kugel是正确的,这仍然是最干净的答案。谢谢
Arif YILMAZ

45

刚刚写了一篇关于此的文章: ASP.NET MVC的多个提交按钮

基本上,不是使用,而是ActionMethodSelectorAttribute使用 ActionNameSelectorAttribute,它允许我假装动作名称是我想要的名称。幸好,ActionNameSelectorAttribute不仅可以让我指定操作名称,还可以选择当前操作是否与请求匹配。

所以有我的课(顺便说一句,我不太喜欢这个名字):

public class HttpParamActionAttribute : ActionNameSelectorAttribute {
    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
} 

要使用,只需定义如下形式:

<% using (Html.BeginForm("Action", "Post")) { %>
  <!— form fields -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %> 

和控制器有两种方法

public class PostController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SaveDraft(…) {
        //…
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Publish(…) {
        //…
    } 
}

如您所见,该属性根本不需要您指定任何内容。同样,按钮的名称直接转换为方法名称。另外(我还没有尝试过)这些也应该像正常操作一样工作,因此您可以直接发布到其中任何一个。


1
美丽!我认为这是最优雅的解决方案。它消除了的的submit考虑标记,这是理想的,因为它是一个应该对控制流无轴承纯的UI属性。而是,name每个submit标签的唯一属性直接转换为控制器上的离散操作方法。
柯克·沃尔

+1对我来说,到目前为止,这是解决此问题的最佳解决方案。自从实现以来,我注意到HttpParamActionAttribut传递了大量流量,但是与Asp.Net MVC在处理请求时必须执行的所有其他操作相比,这是完全可以接受的。要仅破解,我需要在控制器中放置一个空的“ Action”,以防止Resharper警告我“ Action”操作不存在。非常感谢你!
塞缪尔

我查看了所有解决方案,并同意这是一个不错的优雅,简单的解决方案。不错,没有条件语句,并且健壮,可以在有新按钮时定义新的控制器动作。叫我的类MultiButtonActionHandler FYI ;-)
ejhost

36

它很短,很套房:

耶伦·多普Jeroen Dop)回答了

<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />

并在代码背后这样做

 if( Request.Form["submitbutton1"] != null)
{
    // Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
       // code for function 2
}

祝好运。


太棒了 正是我以前在网络表单中所做的。干杯哥们
djack109

比最简单的答案简单得多!谢谢!
pabben

35

我建议有兴趣的人士看看Maarten Balliauw的解决方案。我觉得这很优雅。

万一链接消失,它将使用MultiButton应用于控制器动作的属性来指示该动作应与哪个按钮单击相关。


这是我们现在使用的解决方案,非常简洁。只是MVC 2吗?
西蒙保留

这很漂亮!我以前没看过!虽然我同意您可能希望重新设计使用多个提交而仅使用一个按钮的任何解决方案,但我身处困境,必须这样做。这个答案应该赢了!
Rikon

这是一个很好的解决方案。很干净
Arnej65

尝试了这种方法,但无法使其在MVC3中工作。排名第一的投票者的变体为我工作。
斯科特·劳伦斯

简短而甜蜜..但不适用于mvc 3+
Piotr Kula

21

您应该能够为按钮命名并为其赋予一个值。然后将此名称作为参数映射到操作。或者,使用2个单独的动作链接或2个形式。


到目前为止,我所见过的最干净,最简单的解决方案。
杰森·埃文斯

13

您可以这样写:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

然后在页面中检查名称==“发送”或名称==“取消” ...


1
尽管这是可行的,但我认为拥有两个具有相同名称的元素是错误的做法。
2013年

1
没必要错。这取决于您如何使用输入。您可以有多个具有相同名称的元素,并希望接收多个数据(这是单选按钮和复选框的工作方式)。但是,是的,如果您使用此方法是因为您在执行“错误” ...这就是为什么我放“您可以”而不是“您应该”的原因:P
Ironicnet

12

我不喜欢ActionSelectName的地方是,控制器中的每个操作方法都会调用IsValidName。我不知道为什么会这样。我喜欢一个解决方案,其中每个按钮根据其功能都有不同的名称,但是我不喜欢这样的事实,即操作方法中的参数必须与表单中的按钮一样多。我为所有按钮类型创建了一个枚举:

public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}

我使用ActionFilter代替ActionSelectName:

public class MultipleButtonsEnumAttribute : ActionFilterAttribute
{
    public Type EnumType { get; set; }

    public MultipleButtonsEnumAttribute(Type enumType)
    {
        EnumType = enumType;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        foreach (var key in filterContext.HttpContext.Request.Form.AllKeys)
        {
            if (Enum.IsDefined(EnumType, key))
            {
                var pDesc = filterContext.ActionDescriptor.GetParameters()
                    .FirstOrDefault(x => x.ParameterType == EnumType);
                filterContext.ActionParameters[pDesc.ParameterName] = Enum.Parse(EnumType, key);
                break;
            }
        }
    }
}

筛选器将在表单数据中找到按钮名称,并且如果按钮名称与枚举中定义的任何按钮类型匹配,它将在动作参数中找到ButtonType参数:

[MultipleButtonsEnumAttribute(typeof(ButtonType))]
public ActionResult Manage(ButtonType buttonPressed, ManageViewModel model)
{
    if (button == ButtonType.Cancel)
    {
        return RedirectToAction("Index", "Home");
    }
    //and so on
    return View(model)
}

然后在视图中,我可以使用:

<input type="submit" value="Button Cancel" name="@ButtonType.Cancel" />
<input type="submit" value="Button Submit" name="@ButtonType.Submit" />

11

以下是最适合我的方法:

<input type="submit" value="Delete" name="onDelete" />
<input type="submit" value="Save" name="onSave" />


public ActionResult Practice(MyModel model, string onSave, string onDelete)
{
    if (onDelete != null)
    {
        // Delete the object
        ...
        return EmptyResult();
    }

    // Save the object
    ...
    return EmptyResult();
}

我在控制器方法中获得onDelete和onSave的空值。你知道为什么吗?
Diganta Kumar

如果您单击相应的按钮,则任何一个都不为空。您单击哪个按钮获取空值?
谢尔盖

11

我也遇到过这个“问题”,但是通过添加name属性找到了一个相当合乎逻辑的解决方案。我不记得其他语言有这个问题。

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2

  • ...
  • 如果一个表单包含多个提交按钮,则仅激活的提交按钮成功。
  • ...

意味着以下代码value属性可以更改,本地化,国际化,而无需额外的代码检查强类型资源文件或常量。

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="send" value="Send" />
<input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="draft" value="Save as draft" />
<% Html.EndForm(); %>`

在接收端,您只需要检查您的任何已知提交类型是否不是 null

public ActionResult YourAction(YourModel model) {

    if(Request["send"] != null) {

        // we got a send

    }else if(Request["cancel"]) {

        // we got a cancel, but would you really want to post data for this?

    }else if(Request["draft"]) {

        // we got a draft

    }

}

这是我们选择用于简单Web应用程序的解决方案,在该应用程序中我们需要ASP.NET WebForms功能,但要在MVC内。
BrandonG '16

10

如果您没有使用HTML 5的限制,则可以将<button>标记与formactionAttribute一起使用:

<form action="demo_form.asp" method="get">
   First name: <input type="text" name="fname" /><br />
   Last name: <input type="text" name="lname" /><br />
   <button type="submit">Submit</button><br />
   <button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>

参考:http : //www.w3schools.com/html5/att_button_formaction.asp


9

如果您的浏览器支持输入按钮的属性formaction(IE 10+,不确定其他浏览器),那么以下方法应该起作用:

@using (Html.BeginForm()){
    //put form inputs here

<input id="sendBtn" value="Send" type="submit" formaction="@Url.Action("Name Of Send Action")" />

<input id="cancelBtn" value="Cancel" type="submit" formaction="@Url.Action("Name of Cancel Action") />

}

在下面查看我的答案,它不依赖于规范草案。您的答案确实允许使用不同的操作网址,而我的则不然。
Tom Hofman 2014年

9

您可以通过三种方式解决上述问题

  1. HTML方式
  2. jQuery方式
  3. “ ActionNameSelectorAttribute”方式

下面的视频以演示方式总结了这三种方法。

https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940

HTML方式:-

以HTML方式,我们需要创建两个表单并将“提交”按钮放置在每个表单内。每个表单的动作将指向不同的/各自的动作。您可以看到以下代码,第一个表单将发布到“ Action1”,第二个表单将发布到“ Action2”,具体取决于单击的“提交”按钮。

<form action="Action1" method=post>
<input type=”submit name=”Submit1”/>
</form>

<form action="Action2" method=post>
<input type=”submit name=”Submit2”>
</form>

阿贾克斯的方式:-

如果您是Ajax爱好者,那么第二种选择会让您更加兴奋。以Ajax方式,我们可以创建两个不同的函数“ Fun1”和“ Fun1”,请参见以下代码。这些函数将通过使用JQUERY或任何其他框架进行Ajax调用。这些功能均与“提交”按钮的“ OnClick”事件绑定。这些函数中的每一个都调用各自的动作名称。

<Script language="javascript">
function Fun1()
{
$.post(“/Action1”,null,CallBack1);
}
function Fun2()
{
$.post(“/Action2”,null,CallBack2);
}
</Script>

<form action="/Action1" method=post>
<input type=submit name=sub1 onclick=”Fun2()”/>
</form>
<form action="/Action2" method=post>
<input type=submit name=sub2 onclick=”Fun1()”/>
</form>

使用“ ActionNameSelectorAttribute”:-

这是一个很好的选择。“ ActionNameSelectorAttribute”是一个简单的属性类,我们可以在其中编写决策逻辑来决定可以执行哪个动作。

因此,第一件事是在HTML中,我们需要在提交按钮上加上适当的名称,以便在服务器上识别它们。

您可以看到我们在按钮名称上添加了“保存”和“删除”。您还可以注意到,在操作中,我们只是放置了控制器名称“ Customer”,而不是特定的操作名称。我们希望动作名称由“ ActionNameSelectorAttribute”决定。

<form action=”Customer method=post>
<input type=submit value="Save" name="Save" /> <br />
<input type=submit value="Delete" name="Delete"/>
</form>

因此,当单击“提交”按钮时,它首先命中“ ActionNameSelector”属性,然后根据触发了哪个提交来调用适当的操作。

在此处输入图片说明

因此,第一步是创建一个从“ ActionNameSelectorAttribute”类继承的类。在此类中,我们创建了一个简单的属性“ Name”。

我们还需要覆盖“ IsValidName”函数,该函数返回true或flase。该函数是我们编写是否必须执行动作的逻辑的地方。因此,如果此函数返回true,则执行该动作,否则不执行。

public class SubmitButtonSelector : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
        {
            // Try to find out if the name exists in the data sent from form
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
            if (value != null)
            {
                return true;
            }
            return false;

        }
    }

上面函数的主要核心在下面的代码中。“ ValueProvider”集合具有从表单发布的所有数据。因此,它首先查找“名称”值,如果它在HTTP请求中找到,则返回true,否则返回false。

var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
      {
        return true;
      }
      return false;

然后可以在相应的动作上修饰此属性类,并可以提供相应的“名称”值。因此,如果提交正在执行此操作,并且名称与HTML提交按钮名称匹配,则它将进一步执行该操作,否则将不执行该操作。

public class CustomerController : Controller
{
        [SubmitButtonSelector(Name="Save")]
        public ActionResult Save()
        {
            return Content("Save Called");
        }
        [SubmitButtonSelector(Name = "Delete")]
        public ActionResult Delete()
        {
            return Content("Delete Called");
        }
}


7

这是我要使用的技术,在这里还没有看到。激发该解决方案的链接(由Saajid Ismail发布)为http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form .aspx)。它采用了Dylan Beattie的答案来进行本地化而没有任何问题。

在视图中:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<button name="button" value="send"><%: Resources.Messages.Send %></button>
<button name="button" value="cancel"><%: Resources.Messages.Cancel %></button>
<% Html.EndForm(); %>

在控制器中:

public class MyController : Controller 
{
    public ActionResult MyAction(string button)
    {
         switch(button)
         {
             case "send":
                 this.DoSend();
                 break;
             case "cancel":
                 this.DoCancel();
                 break;
         }
    }
}

看起来像Ironicnet提供的解决方案。
克里斯·范德·玛斯特

当然很相似,但这同时显示了本地化和控制器代码,这是我在该线程中没有看到的。我在寻找方法时发现了这个线程,并想将我想出的东西记录下来,以供可能在同一条船上的其他任何人使用。
mlibby

1
实际上,它与Ironicnet的功能不同。他用<input>元素。我使用<button>,这是本地化所必需的,而没有变量值属性。
mlibby 2010年

7

此脚本允许指定一个data-form-action属性,该属性将在所有浏览器中用作HTML5 formaction属性(以不干扰用户的方式):

$(document).on('click', '[type="submit"][data-form-action]', function(event) {
    var $this = $(this),
    var formAction = $this.attr('data-form-action'),
    $form = $($this.closest('form'));
    $form.attr('action', formAction);             
});

包含按钮的表单将发布到data-form-action属性中指定的URL:

<button type="submit" data-form-action="different/url">Submit</button>   

这需要jQuery 1.7。对于以前的版本,您应该使用live()而不是on()


6

这是我编写的用于处理多个图像和/或文本按钮的扩展方法。

这是图像按钮的HTML:

<input id="btnJoin" name="Join" src="/content/images/buttons/btnJoin.png" 
       type="image">

或输入文本提交按钮:

<input type="submit" class="ui-button green" name="Submit_Join" value="Add to cart"  />
<input type="submit" class="ui-button red" name="Submit_Skip" value="Not today"  />

这是您使用来从控制器调用的扩展方法form.GetSubmitButtonName()。对于图像按钮,它将查找带有的表单参数.x(表示单击了图像按钮)并提取名称。对于常规input按钮,它将查找以开头的名称,Submit_然后从中提取命令。因为我正在抽象确定“命令”的逻辑,所以您可以在客户端上的图像+文本按钮之间切换,而无需更改服务器端代码。

public static class FormCollectionExtensions
{
    public static string GetSubmitButtonName(this FormCollection formCollection)
    {
        return GetSubmitButtonName(formCollection, true);
    }

    public static string GetSubmitButtonName(this FormCollection formCollection, bool throwOnError)
    {
        var imageButton = formCollection.Keys.OfType<string>().Where(x => x.EndsWith(".x")).SingleOrDefault();
        var textButton = formCollection.Keys.OfType<string>().Where(x => x.StartsWith("Submit_")).SingleOrDefault();

        if (textButton != null)
        {
            return textButton.Substring("Submit_".Length);
        }

        // we got something like AddToCart.x
        if (imageButton != null)
        {
            return imageButton.Substring(0, imageButton.Length - 2);
        }

        if (throwOnError)
        {
            throw new ApplicationException("No button found");
        }
        else
        {
            return null;
        }
    }
}

注意:对于文本按钮,您必须在名称前加上Submit_。我之所以喜欢这种方式,是因为它意味着您无需更改代码即可更改文本(显示)值。与SELECT元素不同,INPUT按钮仅具有“值”,而没有单独的“文本”属性。我的按钮在不同的上下文中说的不同,但是映射到相同的“命令”。我更喜欢用这种方式提取名称,而不必为编写代码== "Add to cart"


我喜欢选择检查名称,因为您不能总是检查例如的值。您有一个项目列表,每个项目都有一个“删除”按钮。
马克斯

6

我没有足够的代表在正确的位置发表评论,但我整天都在此发表评论,所以想分享。

尝试实现“ MultipleButtonAttribute”解决方案时,ValueProvider.GetValue(keyValue)将错误地返回null

原来,当我应该将System.Web.MVC版本3.0设置为4.0时(其他程序集是4.0)。我不知道为什么我的项目无法正确升级,并且我没有其他明显的问题。

因此,如果您ActionNameSelectorAttribute不工作,请检查。


6

我参加聚会很晚了,但是这里...我的实现是从@mkozicki借来的,但是需要更少的硬编码字符串才能出错。 需要框架4.5+。本质上,控制器方法名称应该是路由的关键。

标记。按钮名称必须使用"action:[controllerMethodName]"

(请注意使用C#6 nameof API,为要调用的控制器方法的名称提供特定于类型的引用。

<form>
    ... form fields ....
    <button name="action:@nameof(MyApp.Controllers.MyController.FundDeathStar)" type="submit" formmethod="post">Fund Death Star</button>
    <button name="action:@nameof(MyApp.Controllers.MyController.HireBoba)" type="submit" formmethod="post">Hire Boba Fett</button>
</form>

控制器

namespace MyApp.Controllers
{
    class MyController
    {    
        [SubmitActionToThisMethod]
        public async Task<ActionResult> FundDeathStar(ImperialModel model)
        {
            await TrainStormTroopers();
            return View();
        }

        [SubmitActionToThisMethod]
        public async Task<ActionResult> HireBoba(ImperialModel model)
        {
            await RepairSlave1();
            return View();
        }
    }
}

属性魔术。注意CallerMemberName善良的使用。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitActionToThisMethodAttribute : ActionNameSelectorAttribute
{        
    public SubmitActionToThisMethodAttribute([CallerMemberName]string ControllerMethodName = "")
    {
        controllerMethod = ControllerMethodName;
        actionFormat = string.Concat(actionConstant, ":", controllerMethod);
    }
    const string actionConstant = "action";
    readonly string actionFormat;
    readonly string controllerMethod;

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var value = controllerContext.Controller.ValueProvider.GetValue(actionFormat);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[actionConstant] = controllerMethod;
            isValidName = true;
        }
        return isValidName;
    }
}

尽管这是一种不错的方法,但是您将无法使用内置的mvc模型绑定程序,因为它使用了按钮的“名称”属性。
ooXei1sh

该解决方案的目的是解决MVC后期路由。请描述一个改进。
亚伦·休顿

6
[HttpPost]
public ActionResult ConfirmMobile(string nameValueResend, string nameValueSubmit, RegisterModel model)
    {
        var button = nameValueResend ?? nameValueSubmit;
        if (button == "Resend")
        {

        }
        else
        {

        }
    }


    Razor file Content:
    @using (Html.BeginForm()
    {
        <div class="page registration-result-page">

            <div class="page-title">
                <h1> Confirm Mobile Number</h1>
            </div>

            <div class="result">
                @Html.EditorFor(model => model.VefificationCode)
                @Html.LabelFor(model => model.VefificationCode, new { })
                @Html.ValidationMessageFor(model => model.VefificationCode)
            </div>
            <div class="buttons">
                <button type="submit" class="btn" name="nameValueResend" value="Resend">
                    Resend
                </button>
                <button type="submit" class="btn" name="nameValueSubmit" value="Verify">
                    Submit
                </button>

            </div>
            </div>

    }

这是另一个有用的链接,用于解释表单发布的不同方式。
喜马拉雅山Garg

5

我试图对所有解决方案进行综合,并创建了一个[ButtenHandler]属性,该属性使处理表单上的多个按钮变得容易。

我已经在ASP.NET MVC中的 CodeProject 多个参数化(可本地化)表单按钮上对其进行了描述

要处理此按钮的简单情况:

<button type="submit" name="AddDepartment">Add Department</button>

您将拥有类似以下操作方法的内容:

[ButtonHandler()]
public ActionResult AddDepartment(Company model)
{
    model.Departments.Add(new Department());
    return View(model);
}

请注意,按钮的名称如何与操作方法的名称匹配。本文还介绍了如何使按钮具有值和按钮具有索引。


5
//model
    public class input_element
        {
         public string Btn { get; set; }
        }   

//views--submit btn can be input type also...
    @using (Html.BeginForm())
    {
            <button type="submit" name="btn" value="verify">
             Verify data</button>
            <button type="submit" name="btn" value="save">
             Save data</button>    
            <button type="submit" name="btn" value="redirect">
                 Redirect</button>
    }

//controller

    public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";
            return View();
        }

        [HttpPost]
        public ActionResult About(input_element model)
        {
                if (model.Btn == "verify")
                {
                // the Verify button was clicked
                }
                else if (model.Btn == "save")
                {
                // the Save button was clicked
                } 
                else if (model.Btn == "redirect")
                {
                // the Redirect button was clicked
                } 
                return View();
        }

您可能没有意识到这一点,但是这个问题已经多次发布了相同的答案。
安德鲁·巴伯

2
另外,您似乎发布的答案仅包含代码,没有任何解释。您是否考虑添加一些叙述来解释代码为什么起作用,以及什么使其成为问题的答案?这对提出问题的人以及其他任何人都非常有帮助。
安德鲁·巴伯

2
当然可以。但是很久以前,其他人已经给出了这个答案。他们还解释了为什么它也起作用。
Andrew Barber

5

这是我发现的最好方法:

http://iwayneo.blogspot.co.uk/2013/10/aspnet-mvc-action-selector-with-list.html

这是代码:

    /// <summary>
    /// ActionMethodSelector to enable submit buttons to execute specific action methods.
    /// </summary>
    public class AcceptParameterAttribute : ActionMethodSelectorAttribute
   {
        /// <summary>
        /// Gets or sets the value to use to inject the index into
        /// </summary>
       public string TargetArgument { get; set; }

       /// <summary>
       /// Gets or sets the value to use in submit button to identify which method to select. This must be unique in each controller.
       /// </summary>
       public string Action { get; set; }

       /// <summary>
       /// Gets or sets the regular expression to match the action.
       /// </summary>
       public string ActionRegex { get; set; }

       /// <summary>
       /// Determines whether the action method selection is valid for the specified controller context.
       /// </summary>
       /// <param name="controllerContext">The controller context.</param>
       /// <param name="methodInfo">Information about the action method.</param>
       /// <returns>true if the action method selection is valid for the specified controller context; otherwise, false.</returns>
       public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
       {

           if (controllerContext == null)
           {
               throw new ArgumentNullException("controllerContext");
           }

           Func<NameValueCollection> formGetter;
           Func<NameValueCollection> queryStringGetter;

           ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);

           var form = formGetter();
           var queryString = queryStringGetter();

           var req = form.AllKeys.Any() ? form : queryString;

           if (!string.IsNullOrEmpty(this.ActionRegex))
           {
               foreach (var key in req.AllKeys.Where(k => k.StartsWith(Action, true, System.Threading.Thread.CurrentThread.CurrentCulture)))
               {
                   if (key.Contains(":"))
                   {
                       if (key.Split(':').Count() == this.ActionRegex.Split(':').Count())
                       {
                           bool match = false;
                           for (int i = 0; i < key.Split(':').Count(); i++)
                           {
                               if (Regex.IsMatch(key.Split(':')[0], this.ActionRegex.Split(':')[0]))
                               {
                                   match = true;
                               }
                               else
                               {
                                   match = false;
                                   break;
                               }
                           }

                           if (match)
                           {
                               return !string.IsNullOrEmpty(req[key]);
                           }
                       }
                   }
                   else
                   {
                       if (Regex.IsMatch(key, this.Action + this.ActionRegex))
                       {
                           return !string.IsNullOrEmpty(req[key]);
                       }
                   }

               }
               return false;
           }
           else
           {
               return req.AllKeys.Contains(this.Action);
           }
       }
   }

享受无代码气味的多提交按钮。

谢谢


链接目前破,这是最接近的存档版本我能找到的:web.archive.org/web/20110706230408/http://blogs.sonatribe.com/...
伊恩·坎普

嗨伊恩-感谢挖了这一点-我在这里转贴它:iwayneo.blogspot.co.uk/2013/10/...

4

HttpParamActionAttribute方法的修改版本,但已修复错误,不会在过期/无效的会话回发中引起错误。要查看当前站点是否存在问题,请在窗口中打开表单,然后单击SavePublish,打开一个重复的窗口,然后注销。现在返回您的第一个窗口,并尝试使用任一按钮提交表单。对我来说,我遇到了一个错误,因此此更改为我解决了该问题。为了简洁起见,我忽略了很多东西,但是您应该明白这一点。关键部分是ActionName属性中的包含,并确保传入的名称是显示表单的视图的名称。

属性类别

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
    private readonly string actionName;

    public HttpParamActionAttribute(string actionName)
    {
        this.actionName = actionName;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals(this.actionName, StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
}

控制者

[Authorize(Roles="CanAddContent")]
public ActionResult CreateContent(Guid contentOwnerId)
{
    var viewModel = new ContentViewModel
    {
        ContentOwnerId = contentOwnerId
        //populate rest of view model
    }
    return View("CreateContent", viewModel);
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult SaveDraft(ContentFormModel model)
{
    //Save as draft
    return RedirectToAction("CreateContent");
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult Publish(ContentFormModel model)
{
    //publish content
    return RedirectToAction("CreateContent");
}

视图

@using (Ajax.BeginForm("CreateContent", "MyController", new { contentOwnerId = Model.ContentOwnerId }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(x => x.ContentOwnerId)

    <!-- Rest of your form controls -->
    <input name="SaveDraft" type="submit" value="SaveDraft" />
    <input name="Publish" type="submit" value="Publish" />
}

3

我的JQuery方法使用扩展方法:

public static MvcHtmlString SubmitButtonFor<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string value) where TController : Controller
{
    RouteValueDictionary routingValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);

    var onclick = string.Format("$('form').first().attr('action', '/{0}')", string.Join("/", routingValues.Values.ToArray().Where(x => x != null).Select(x => x.ToString()).ToArray()));
    var html = "<input type=\"submit\" value=\"" + value + "\" onclick=\"" + onclick + "\" />";

    return MvcHtmlString.Create(html);
}

您可以像这样使用它:

@(Html.SubmitButtonFor<FooController>(c => c.Save(null), "Save"))

它呈现如下:

<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >

3

对于每个提交按钮,只需添加:

$('#btnSelector').click(function () {

    $('form').attr('action', "/Your/Action/);
    $('form').submit();

});

3

根据mkozicki的答案,我提出了一些不同的解决方案。我仍然使用,ActionNameSelectorAttribute但是我需要处理两个按钮“保存”和“同步”。它们几乎相同,所以我不想执行两个操作。

属性

public class MultipleButtonActionAttribute : ActionNameSelectorAttribute
{        
    private readonly List<string> AcceptedButtonNames;

    public MultipleButtonActionAttribute(params string[] acceptedButtonNames)
    {
        AcceptedButtonNames = acceptedButtonNames.ToList();
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {            
        foreach (var acceptedButtonName in AcceptedButtonNames)
        {
            var button = controllerContext.Controller.ValueProvider.GetValue(acceptedButtonName);
            if (button == null)
            {
                continue;
            }                
            controllerContext.Controller.ControllerContext.RouteData.Values.Add("ButtonName", acceptedButtonName);
            return true;
        }
        return false;
    }
}

视图

<input type="submit" value="Save" name="Save" />
<input type="submit" value="Save and Sync" name="Sync" />

控制者

 [MultipleButtonAction("Save", "Sync")]
 public ActionResult Sync(OrgSynchronizationEditModel model)
 {
     var btn = this.RouteData.Values["ButtonName"];

我还想指出,如果动作做不同的事情,我可能会遵循mkozicki的帖子。


1

我为HtmlHelper创建了一个ActionButton方法。它将在OnClick事件中生成带有少量javascript的普通输入按钮中会将表单提交给指定的Controller / Action。

你这样使用助手

@Html.ActionButton("MyControllerName", "MyActionName", "button text")

这将生成以下HTML

<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">

这是扩展方法代码:

VB.Net

<System.Runtime.CompilerServices.Extension()>
Function ActionButton(pHtml As HtmlHelper, pAction As String, pController As String, pRouteValues As Object, pBtnValue As String, pBtnName As String, pBtnID As String) As MvcHtmlString
    Dim urlHelperForActionLink As UrlHelper
    Dim btnTagBuilder As TagBuilder

    Dim actionLink As String
    Dim onClickEventJavascript As String

    urlHelperForActionLink = New UrlHelper(pHtml.ViewContext.RequestContext)
    If pController <> "" Then
        actionLink = urlHelperForActionLink.Action(pAction, pController, pRouteValues)
    Else
        actionLink = urlHelperForActionLink.Action(pAction, pRouteValues)
    End If
    onClickEventJavascript = "this.form.action = '" & actionLink & "'; this.form.submit();"

    btnTagBuilder = New TagBuilder("input")
    btnTagBuilder.MergeAttribute("type", "button")

    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript)

    If pBtnValue <> "" Then btnTagBuilder.MergeAttribute("value", pBtnValue)
    If pBtnName <> "" Then btnTagBuilder.MergeAttribute("name", pBtnName)
    If pBtnID <> "" Then btnTagBuilder.MergeAttribute("id", pBtnID)

    Return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal))
End Function

C#(C#代码只是从VB DLL中反编译,因此可以得到一些美化……但是时间太短了:-))

public static MvcHtmlString ActionButton(this HtmlHelper pHtml, string pAction, string pController, object pRouteValues, string pBtnValue, string pBtnName, string pBtnID)
{
    UrlHelper urlHelperForActionLink = new UrlHelper(pHtml.ViewContext.RequestContext);
    bool flag = Operators.CompareString(pController, "", true) != 0;
    string actionLink;
    if (flag)
    {
        actionLink = urlHelperForActionLink.Action(pAction, pController, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    else
    {
        actionLink = urlHelperForActionLink.Action(pAction, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    string onClickEventJavascript = "this.form.action = '" + actionLink + "'; this.form.submit();";
    TagBuilder btnTagBuilder = new TagBuilder("input");
    btnTagBuilder.MergeAttribute("type", "button");
    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript);
    flag = (Operators.CompareString(pBtnValue, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("value", pBtnValue);
    }
    flag = (Operators.CompareString(pBtnName, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("name", pBtnName);
    }
    flag = (Operators.CompareString(pBtnID, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("id", pBtnID);
    }
    return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal));
}

这些方法具有各种参数,但是为了易于使用,您可以创建一些重载,这些重载仅采用您需要的参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.