你如何处理ASP.NET MVC框架中的多个提交button?
是否有一些简单的方法来处理同一个表单中的多个提交button? 例:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %> <input type="submit" value="Send" /> <input type="submit" value="Cancel" /> <% Html.EndForm(); %>
任何想法如何在ASP.NET Framework Beta中做到这一点? 我所search的所有例子都有单个button。
这是一个基本上干净的基于属性的解决scheme的多重提交button问题,很大程度上基于Maartin Balliauw的post和评论。
[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) { ... }
更新: Razor页面看起来提供了相同的function。 对于新的发展,这可能是更好的。
给你的提交button一个名字,然后检查你的控制器方法提交的值:
<% 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")); } }
编辑:
为了扩展这种方法来处理本地化的网站,在别的地方隔离你的消息(比如编译一个资源文件到一个强types的资源类)
然后修改代码,使其如下工作:
<% 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()); }
你可以像上面提到的那样在动作中检查名字,但是你可以考虑这是否是好的devise。 考虑行为的责任是一个好主意,不要将这个devise与UIbutton名称等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(); %>
此外,在“取消”的情况下,你通常只是不处理的forms,并正在去一个新的URL。 在这种情况下,您不需要提交表单,只需要一个链接:
<%=Html.ActionLink("Cancel", "List", "MyController") %>
Eilonbuild议你可以这样做:
如果您有多个button,您可以通过给每个button一个名称来区分它们:
<input type="submit" name="SaveButton" value="Save data" /> <input type="submit" name="CancelButton" value="Cancel and go back to main page" />
在您的控制器操作方法中,您可以添加以HTMLinput标记名称命名的参数:
public ActionResult DoSomeStuff(string saveButton, string cancelButton, ... other parameters ...) { ... }
如果有任何值被发送到其中一个参数,这意味着button是被点击的那个。 网页浏览器将只发布一个点击一个button的值。 所有其他值将为空。
if (saveButton != null) { /* do save logic */ } if (cancelButton != null) { /* do cancel logic */ }
我喜欢这个方法,因为它不依赖于提交button的值属性,它比指定的名称更有可能改变,并且不需要javascript被启用
请参阅: http : //forums.asp.net/p/1369617/2865166.aspx#2865166
刚刚写了一篇文章: 与ASP.NET MVC多个提交button :
基本上,而不是使用ActionMethodSelectorAttribute
,我使用ActionNameSelectorAttribute
,它允许我假装行动名称是我想要的。 幸运的是, ActionNameSelectorAttribute
不只是让我指定操作名称,而是可以select当前操作是否匹配请求。
所以有我的课(顺便说一句,我不喜欢这个名字):
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(…) { //… } }
如你所见,这个属性并不要求你指定任何东西。 此外,button的名称直接转换为方法名称。 另外(我还没有尝试过),这些应该作为正常的行为,所以你可以直接发布到任何一个。
我build议感兴趣的各方看看马尔滕·鲍利奥的解决scheme 。 我觉得这很优雅。
如果链接消失,则使用应用于控制器操作的MultiButton
属性来指示该操作应与哪个button单击相关。
它是短和套房:
Jeroen Dop回答了这个问题
<input type="submit" name="submitbutton1" value="submit1" /> <input type="submit" name="submitbutton2" value="submit2" />
并在代码behinde这样做
if( Request.Form["submitbutton1"] != null) { // Code for function 1 } else if(Request.Form["submitButton2"] != null ) { // code for function 2 }
祝你好运。
你应该能够命名button,并给他们一个价值; 然后将该名称作为参数映射到该操作。 或者,使用2个独立的操作链接或2个表单。
你可以写:
<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %> <input type="submit" name="button" value="Send" /> <input type="submit" name="button" value="Cancel" /> <% Html.EndForm(); %>
然后在页面中检查名称==“发送”或名称==“取消”…
对于ActionSelectName,我不喜欢的是IsValidName被调用控制器中的每个操作方法; 我不知道它为什么这样工作。 我喜欢一个解决scheme,每个button都有一个不同的名字,但是我不喜欢这样一个事实:在action方法中必须包含与表单中的button一样多的参数。 我为所有buttontypes创build了一个枚举:
public enum ButtonType { Submit, Cancel, Delete }
而不是ActionSelectName,我使用一个ActionFilter:
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; } } } }
filter将在表单数据中findbutton名称,如果button名称与枚举中定义的任何buttontypes相匹配,则会在操作参数中findButtonType参数:
[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" />
这里是最适合我的东西:
<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(); }
我遇到了这个“问题”,但通过添加name
属性find了一个相当合理的解决scheme。 我不记得有其他语言的这个问题。
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
- …
- 如果表单包含多个提交button,则只有激活的提交button成功。
- …
这意味着下面的代码value
属性可以被改变,本地化,国际化,而不需要额外的代码检查强types的资源文件或常量。
<% 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(); %>`
在接收端,您只需检查是否有任何已知的提交types不为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 } }
大卫·芬德利(David Findley)在他的ASP.Net博客上写了大约3种不同的选项。
阅读相同forms的多个button文章,看看他的解决scheme,以及每个的优点和缺点。 恕我直言,他提供了一个非常优雅的解决scheme,使用属性,你装饰你的行动。
这是我使用的技术,我还没有看到它。 启动这个解决scheme的链接(由Saajid Ismail发布)是http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-sameform .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; } } }
如果您对HTML 5的使用没有限制,那么可以使用带有formaction
的<button>
标签属性:
<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
这个脚本允许指定一个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); });
包含该button的表单将被发布到在data-form-action属性中指定的URL:
<button type="submit" data-form-action="different/url">Submit</button>
这需要jQuery 1.7。 对于以前的版本,你应该使用live()
而不是on()
。
有三种方法可以解决上述问题
- HTML方式
- jquery的方式
- “ActionNameSelectorAttribute”的方式
以下是以演示的方式总结所有三种方法的video。
https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/809335512483940
HTML方式: –
在HTML方式中,我们需要创build两个表单,并在每个表单中放置“提交”button。 每个表单的动作都会指向不同的/相应的动作。 您可以看到下面的代码,第一个表单发布到“Action1”,第二个表单将发布到“Action2”,具体取决于点击“Submit”button。
<form action="Action1" method=post> <input type=”submit” name=”Submit1”/> </form> <form action="Action2" method=post> <input type=”submit” name=”Submit2”> </form>
Ajax方式: –
如果你是一个阿贾克斯爱好者,这第二个选项会激发你更多。 以Ajax的方式,我们可以创build两个不同的函数“Fun1”和“Fun1”,请参阅下面的代码。 这些函数将通过使用JQUERY或任何其他框架来进行Ajax调用。 每个function都绑定了“提交”button的“点击”事件。 每个函数都会调用相应的操作名称。
<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”: –
这是一个很好的和干净的select。 “ActionNameSelectorAttribute”是一个简单的属性类,我们可以写决策逻辑来决定哪个动作可以执行。
所以第一件事是在HTML中,我们需要把正确的名字放在提交button上,以便在服务器上识别它们。
你可以看到我们已经把“保存”和“删除”button名称。 你也可以注意到,在我们刚刚把控制器名称“客户”,而不是一个特定的行动名称的行动。 我们期望动作名称将由“ActionNameSelectorAttribute”决定。
<form action=”Customer” method=post> <input type=submit value="Save" name="Save" /> <br /> <input type=submit value="Delete" name="Delete"/> </form>
所以当提交button被点击时,它首先击中“ActionNameSelector”属性,然后根据哪个提交被触发,调用适当的动作。
所以第一步是创build一个inheritance自“ActionNameSelectorAttribute”类的类。 在这个类中我们创build了一个简单的属性“Name”。
我们还需要覆盖返回true或flase的“IsValidName”函数。 这个函数是我们写逻辑的地方,无论是否需要执行一个动作。 所以如果这个函数返回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; } }
上述function的主要内容在下面的代码中。 “ValueProvider”集合包含从表单发布的所有数据。 所以它首先查找“Name”值,如果它在HTTP请求中find,则返回true,否则返回false。
var value = controllerContext.Controller.ValueProvider.GetValue(Name); if (value != null) { return true; } return false;
然后可以在相应的动作上对该属性类进行修饰,并且可以提供相应的“名称”值。 因此,如果提交正在执行此操作,并且名称与HTML提交button名称相匹配,则会继续执行该操作,否则不会执行该操作。
public class CustomerController : Controller { [SubmitButtonSelector(Name="Save")] public ActionResult Save() { return Content("Save Called"); } [SubmitButtonSelector(Name = "Delete")] public ActionResult Delete() { return Content("Delete Called"); } }
这是我写的处理多个图像和/或文本button的扩展方法。
这是一个图像button的HTML:
<input id="btnJoin" name="Join" src="/contenthttp://img.dovov.combuttons/btnJoin.png" type="image">
或者用于文本提交button:
<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()
从控制器调用的扩展方法。 对于图像button,它会使用.x
(表示图像button被单击)查找表单参数并提取名称。 对于常规input
button,它会查找以Submit_
开头的名称, Submit_
提取命令。 因为我正在抽象确定“命令”的逻辑,所以可以在客户机上的图像+文本button之间进行切换,而无需更改服务器端代码。
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; } } }
注意:对于文本button,您必须在Submit_
名称。 因为我更喜欢这种方式,这意味着您可以更改文本(显示)的值,而无需更改代码。 与SELECT
元素不同, INPUT
button只有一个“值”,没有单独的“文本”属性。 我的button在不同的上下文中说不同的东西 – 但映射到相同的“命令”。 我更喜欢用这种方式提取名称,而不是编写== "Add to cart"
。
我没有足够的代表在正确的地方评论,但我花了一整天在这个,所以想分享。
在尝试实现“MultipleButtonAttribute”解决scheme时, ValueProvider.GetValue(keyValue)
将错误地返回null
。
原来我引用System.Web.MVC版本3.0时它应该是4.0(其他程序集是4.0)。 我不知道为什么我的项目没有正确升级,我没有其他明显的问题。
所以,如果你的ActionNameSelectorAttribute
不工作…检查。
我试图做出所有解决scheme的综合,并创build了一个[ButtenHandler]属性,可以很容易地处理表单上的多个button。
我已经在CodeProject上描述了它在ASP.NET MVC中的多个参数化(本地化)表单button 。
要处理这个button的简单情况:
<button type="submit" name="AddDepartment">Add Department</button>
你将会有如下的操作方法:
[ButtonHandler()] public ActionResult AddDepartment(Company model) { model.Departments.Add(new Department()); return View(model); }
注意button的名称如何匹配操作方法的名称。 本文还介绍了如何使用带索引的值和button。
HttpParamActionAttribute
方法的修改版本,但是在过期/无效会话回发时不会导致错误。 要查看您的当前网站是否存在问题,请在窗口中打开表单,然后单击Save
或Publish
,打开重复窗口并注销。 现在回到您的第一个窗口,并尝试使用任一button提交您的表单。 对我来说,我得到了一个错误,所以这个改变为我解决了这个问题。 为了简洁起见,我省略了一些东西,但是你应该明白这一点。 关键部分是在属性中包含ActionName
,并确保传入的名称是显示表单的View的名称
属性类
[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" /> }
this is the best way that i have found:
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); } } }
Enjoy a code-smell-less multi submit button future.
谢谢
If your browser supports the attribute formaction for input buttons (IE 10+, not sure about other browsers) then the following should work:
@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") /> }
I'm pretty late to the party, but here goes… My implementation borrows from @mkozicki but requires less hardcoded strings to get wrong. Framework 4.5+ required . Essentially, the controller method name should be the key to the routing.
Markup . The button name must be keyed with "action:[controllerMethodName]"
(notice the use of the C#6 nameof API, providing type-specific reference to the name of the controller method you wish to invoke.
<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(); } } }
Attribute Magic . Notice the use of CallerMemberName
goodness.
[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; } }
//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(); }
My JQuery approach using an extension method:
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"))
And it renders like this:
<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >
For each submit button just add:
$('#btnSelector').click(function () { $('form').attr('action', "/Your/Action/); $('form').submit(); });
Based on mkozicki answer I come up with a bit different solution. I still use ActionNameSelectorAttribute
But I needed to handle two buttons 'Save' and 'Sync'. They do almost the same so I didn't want to have two actions.
attribute :
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"];
I also want to point out that if actions do different things I would probably follow mkozicki post.
When using ajax forms, we can use ActionLinks with POST HttpMethod and serialize the form in the AjaxOptions.OnBegin event.
Let's say you have two actions, InsertAction and UpdateAction:
<form> @Html.Hidden("SomeField", "SomeValue") @Ajax.ActionLink( "Insert", "InsertAction", null, new AjaxOptions { OnBegin = "OnBegin", UpdateTargetId = "yourDiv", HttpMethod = "POST" }) @Ajax.ActionLink( "Update", "UpdateAction", null, new AjaxOptions { OnBegin = "OnBegin", UpdateTargetId = "yourDiv", HttpMethod = "POST" }) </form>
使用Javascript
function OnBegin(xhr, settings) { settings.data = $("form").serialize(); }
I've created an ActionButton method for the HtmlHelper . It will generate normal input button with a bit of javascript in the OnClick event that will submit the form to the specified Controller/Action.
You use the helper like that
@Html.ActionButton("MyControllerName", "MyActionName", "button text")
this will generate the following HTML
<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">
Here is the extension method code:
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# (the C# code is just decompiled from the VB DLL, so it can get some beautification… but time is so short :-))
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)); }
These methods have various parameters, but for the ease of use you can create some overload that take just the parameters you need.