ASP.NET MVC – Multiple buttons in the same form

I keep seeing this question in forums and on twitter so I thought I’d post all the various ways you can handle this and what the pros and cons are.

The Scenario

Imagine you have a user signup form. There are several textbox fields for entering the new account information and then two buttons: Signup and Cancel. Signup will process the account information and Cancel will return the user to the home page.

Option 1 – Each button submits the form but provides a different value

~/Views/Account/Register.aspx

  1: <% using (Html.BeginForm()) { %>
  2:     <div>
  3:         <fieldset>
  4:             <legend>Account Information</legend>
  5:             <p>
  6:                 <label for="username">Username:</label>
  7:                 <%= Html.TextBox("username") %>
  8:                 <%= Html.ValidationMessage("username") %>
  9:             </p>
 10:             <p>
 11:                 <label for="email">Email:</label>
 12:                 <%= Html.TextBox("email") %>
 13:                 <%= Html.ValidationMessage("email") %>
 14:             </p>
 15:             <p>
 16:                 <label for="password">Password:</label>
 17:                 <%= Html.Password("password") %>
 18:                 <%= Html.ValidationMessage("password") %>
 19:             </p>
 20:             <p>
 21:                 <label for="confirmPassword">Confirm password:</label>
 22:                 <%= Html.Password("confirmPassword") %>
 23:                 <%= Html.ValidationMessage("confirmPassword") %>
 24:             </p>
 25:                 <button name="button" value="register">Register</button>
 26:                 <button name="button" value="cancel">Cancel</button>
 27:             </p>
 28:         </fieldset>
 29:     </div>
 30:  <% } %>

~/Controllers/AccountController.cs

  1: [AcceptVerbs(HttpVerbs.Post)]
  2: public ActionResult Register(string button, string userName, string email, string password, string confirmPassword)
  3: {
  4:     if (button == "cancel")
  5:         return RedirectToAction("Index", "Home");
  6: 
  7:     ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
  8: 
  9:     if (ValidateRegistration(userName, email, password, confirmPassword))
 10:     {
 11:         // Attempt to register the user
 12:         MembershipCreateStatus createStatus = MembershipService.CreateUser(userName, password, email);
 13: 
 14:         if (createStatus == MembershipCreateStatus.Success)
 15:         {
 16:             FormsAuth.SignIn(userName, false /* createPersistentCookie */);
 17:             return RedirectToAction("Index", "Home");
 18:         }
 19:         else
 20:         {
 21:             ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
 22:         }
 23:     }
 24: 
 25:     // If we got this far, something failed, redisplay form
 26:     return View();
 27: }

The downside to this solution is that you have to add some yucky conditional logic to your controller and all the form data has to be submitted to the server just so the server can issue a redirect. To make the controller code a little better you could implement a custom ActionMethodSelectorAttribute like this:

  1: public class AcceptParameterAttribute : ActionMethodSelectorAttribute
  2: {
  3:     public string Name { get; set; }
  4:     public string Value { get; set; }
  5: 
  6:     public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
  7:     {
  8:         var req = controllerContext.RequestContext.HttpContext.Request;
  9:         return req.Form[this.Name] == this.Value;
 10:     }
 11: }
 12: 

Now I can split into two action methods like this:

  1: [ActionName("Register")]
  2: [AcceptVerbs(HttpVerbs.Post)]
  3: [AcceptParameter(Name="button", Value="cancel")]
  4: public ActionResult Register_Cancel()
  5: {
  6:     return RedirectToAction("Index", "Home");
  7: }
  8: 
  9: [AcceptVerbs(HttpVerbs.Post)]
 10: [AcceptParameter(Name="button", Value="register")]
 11: public ActionResult Register(string userName, string email, string password, string confirmPassword)
 12: {
 13:   // process registration
 14: }

Again, this isn’t the most efficient method but it does let you handle different buttons with different controller methods.

Option 2 – A second form

  1: <% using (Html.BeginForm()) { %>
  2:     <div>
  3:         <fieldset>
  4:             <legend>Account Information</legend>
  5:             <p>
  6:                 <label for="username">Username:</label>
  7:                 <%= Html.TextBox("username") %>
  8:                 <%= Html.ValidationMessage("username") %>
  9:             </p>
 10:             <p>
 11:                 <label for="email">Email:</label>
 12:                 <%= Html.TextBox("email") %>
 13:                 <%= Html.ValidationMessage("email") %>
 14:             </p>
 15:             <p>
 16:                 <label for="password">Password:</label>
 17:                 <%= Html.Password("password") %>
 18:                 <%= Html.ValidationMessage("password") %>
 19:             </p>
 20:             <p>
 21:                 <label for="confirmPassword">Confirm password:</label>
 22:                 <%= Html.Password("confirmPassword") %>
 23:                 <%= Html.ValidationMessage("confirmPassword") %>
 24:             </p>
 25:             <p>
 26:                 <button name="button">Register</button>
 27:                 <button name="button" type="button" onclick="$('#cancelForm').submit()">Cancel</button>
 28:             </p>
 29:         </fieldset>
 30:     </div>
 31: <% } %>
 32: <% using (Html.BeginForm("Register_Cancel", "Account", FormMethod.Post, new { id="cancelForm" })) {} %>
 33: 

All I did here was add a new form after the registration form and point it at my other controller action. I then changed the cancel button to type=”button” so that it would try to submit the form it was sitting in and added an onlick that uses a simple jQuery expression to submit my other “cancel” form. This is more efficient now that it wont submit all the registration data but it is still not the most efficient since it is still using the server to do a redirect.

Option 3: All client side script

  1: <p>
  2:     <button name="button">Register</button>
  3:     <button name="button" type="button" onclick="document.location.href=$('#cancelUrl').attr('href')">Cancel</button>
  4:     <a id="cancelUrl" href="<%= Html.AttributeEncode(Url.Action("Index", "Home")) %>" style="display:none;"></a>
  5: </p>
  6: 

This is the most efficient way to handle the cancel button. There is no interaction with the server to get the url to redirect to. I rendered a hidden <a> tag to contain the url but still used the <button> and some script so that the cancel option still looked like a button on the form. It would also work if I just displayed the <a> tag instead of the button. I’ve noticed several sites that have buttons for actions that submit data and links for actions like cancel that do not. I bet it has to do with this same sort of problem.

Conclusion

When I write MVC applications I use this criteria for choosing what a button should do:

  • Does the button apply to the form that it is sitting in? e.g. “Save”, “Update”, “Ok”, “Submit”
    Use a standard <button> element.
  • Does the button need to post some other data to the server? e.g. “Delete”
    Use a  <button> element with type=”button” and and onclick handler that submits another form with hidden input elements. more on this in a follow-up blog post
  • Is the button purely navigational? e.g. “Cancel”, “Back”, “Next”, “Prev”
    Use and <a> element or a <button> with script to simply navigate the browser to the new location
Published Sunday, May 31, 2009 8:04 AM by findleyd
Filed under: ,

Comments

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, June 1, 2009 12:16 AM by lenocin

I had to do this also lately, and think I'm going to be applying the [AcceptParameter] attribute (a new one too me).

Not really a fan of adding inline JavaScript. I don't know.  Am I wrong to be be against using JavaScript as much as possible (I only use JavaScript for enhancements?)

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, June 8, 2009 12:11 AM by Danny

Just what the doctor ordered !

Are you the same person teaching the 10 part MVC series

for Bob T. ?

Thanks

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, June 8, 2009 8:40 AM by findleyd

Yep thats me. Hope you're enjoying the series.

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, June 8, 2009 5:15 PM by yazılım

Really cool to learn about ActionMethodSelectorAttribute. Thanks for sharing

# re: ASP.NET MVC – Multiple buttons in the same form

Sunday, June 14, 2009 1:43 AM by Boris

The 1st solution was awesome!

Thanks for sharing!

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, June 22, 2009 12:53 PM by John H. Goode

Why not use jQuery, like so:

   $(document).ready(function() {

       $("#btnCancel").click(function() {

           window.location = editUrl;

       });

   });

No need for hyperlink hack!

# re: ASP.NET MVC – Multiple buttons in the same form

Wednesday, August 19, 2009 12:17 PM by Tanrikut

Just what I am looking for. First solution is great for for no javascript situations.

# re: ASP.NET MVC – Multiple buttons in the same form

Wednesday, September 9, 2009 4:17 AM by Markus

Great article, David. However, option 1 does not seem to work if the button text differs from the value:

HTML:

<button type="submit" name="submitButton" value="next" class="btn_red_high"><%=Server.HtmlEncode(Resources.LocalizedResources.BUTTON_NEXT)%></button>

The text for the "next" button is language dependent. Option 1 always returns the text to the controller and not the value, i.e. the variable submitButton has the value of the expression <%=Server.HtmlEncode(Resources.LocalizedResources.BUTTON_NEXT)%> and not the value of the button element. Does anybody else encounter the same behaviour?

# re: ASP.NET MVC – Multiple buttons in the same form

Wednesday, September 9, 2009 5:10 AM by Markus

Addendum to my previous post. The multiple buttons value issue in multilingual pages is now resolved. The key is to give each button an unique name. Conforming to HTTP standards, only the clicked button value is posted to the controller. The controller checks for null (vb=nothing) against each submit button and executes the relevant code:

Page:

<button type="submit" name="addButton" value="add">Button Text or Image</button>

<button type="submit" name="saveButton" value="save">Button Text or Image</button>

<button type="submit" name="nextButton" value="next">Button Text or Image</button>

Controller:

   <AcceptVerbs(HttpVerbs.Post)> _

   Function MyAction(ByVal model As MyModel, ByVal addButton As String, ByVal saveButton As String, ByVal nextButton As String) As ActionResult

           If Not IsNothing(nextButton) Then

               'next code

           ElseIf Not IsNothing(addButton) Then

               'add code

           ElseIf Not IsNothing(saveButton) Then

               'save code

           Else

               'Else Code

           End If

.

.

.

End Function

I hope this helps anyone having the same issue.

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, October 12, 2009 11:32 AM by Simon

This works great unless you are validating the form using jQuery. As a result I just get a null value for the button.

# re: ASP.NET MVC – Multiple buttons in the same form

Saturday, October 24, 2009 1:50 AM by sureshkumar

Thank you very much..

# re: ASP.NET MVC – Multiple buttons in the same form

Sunday, November 22, 2009 9:54 AM by Vadim

Thanks! I changed attribute to work with localised buttons:

<button name="register" value="register">Register</button>

<button name="cancel" value="cancel">Cancel</button>

...

public class AcceptParameterAttribute : ActionMethodSelectorAttribute

   {

       public string Name { get; set; }

       public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)

       {

           var req = controllerContext.RequestContext.HttpContext.Request;

           return req.Form[this.Name] != null;

       }

   }

...

Then you can use it:

[AcceptVerbs(HttpVerbs.Post)]

[AcceptParameter(Name = "register")]

public ActionResult Register(string userName)

...

[AcceptVerbs(HttpVerbs.Post)]

[AcceptParameter(Name = "cancel")]

public ActionResult Register(string userName)

...

# re: ASP.NET MVC – Multiple buttons in the same form

Sunday, January 10, 2010 7:22 AM by Car Market

I thing the ActionMethodSelectorAttribute usage is the best way to handle multiple submit requests from same page. I my case I need to "Save" temporarly and I need to "Publish" which result in two different actions executed ... .

thanks for sharing ... you saved the day !

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, March 18, 2010 8:22 AM by Ajmal Mohamed

Thanks. Its helped me.

# re: ASP.NET MVC – Multiple buttons in the same form

Friday, June 25, 2010 12:55 AM by Kiran

Thanks for the article...

the 1st solution was exactly the one i was looking for.

thank you

# re: ASP.NET MVC – Multiple buttons in the same form

Wednesday, July 7, 2010 12:39 PM by Jihed Halimi

Thanks!

Great post...short and sweet

Thanks once again!!!

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, November 23, 2010 12:14 AM by Navish

Thanks... The option 3 works pretty well...

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, November 23, 2010 8:07 AM by Sam

Excellent separation of concerns in the first approach, really helped us tidy up some messy actions (we have to support js free browsers). Thanks!

# re: ASP.NET MVC – Multiple buttons in the same form

Sunday, January 23, 2011 1:26 PM by dave prout

I like option 2 best, but I can't find a way to write unit tests that set ActionMethodSelectorAttribute before calling the controller action. So I went for option 1, as it's easy to write tests that just pass a string as the first parameter for the controller (second parameter being the model).

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, February 8, 2011 3:09 PM by sthaival

The first option does not work under IE7..Anyone else has the same problem...

# re: ASP.NET MVC – Multiple buttons in the same form

Sunday, February 27, 2011 10:07 AM by Silvia Patteet

Option 3 works very good! Thanks for this solution. I've been looking for a working solution since hours and finally came up to something useful with this article.

I use it with:

<input type="button" value="Continue shopping" class="blackbutton" onclick="document.location.href=$('#continue').attr('href')" />

<a id="continue" href="<%: Model.ReturnUrl %>" style="display:none;"></a>

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, March 3, 2011 9:55 PM by Emran

What a HELL MVC !! maan, it is just going back to the stoneage. I love MVC pattern, but not the ASP.NET MVC framework. I implement MVC pattern in my Web Form application in my own way and it is possible. MVC vouce for separation of concern, but forcing me to write code in the VIEW page !! What a ridiculous point of view !! !! I love WEB Forms and my Heart will go on for Web Form all the time.

# re: ASP.NET MVC – Multiple buttons in the same form

Friday, March 25, 2011 4:28 AM by Manjula

I am trying to use ActionMethodSelectorAttribute ..but wen i implement,i gives an error on Method info parameter.It says The type or namespace name 'MethodInfo' could not be found.

Can any one help please

# re: ASP.NET MVC – Multiple buttons in the same form

Friday, May 6, 2011 8:32 AM by Varga Tamás from Budapest

using System.Reflection

It'll help. I guess google was down when you came to the problem;)

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, June 2, 2011 3:16 PM by Tom

Option 3 is awful.  Ever heard of unobtrusive javascript?

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, June 7, 2011 4:40 AM by D.Aizawa

I have to do is close to the process.

Thanks

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, July 26, 2011 6:08 PM by NewtoMVC

Nice post...The third solution works for me. However i have an added requirement. When the user hits Cancel button, a confirm message with Yes/No buttons needs to be shown. If User selects Yes, user will be redirected to another page and if user selects no, should stay on the same page.

Can you please let me know how to accomplish that?

# re: ASP.NET MVC – Multiple buttons in the same form

Wednesday, July 27, 2011 5:56 PM by findleyd

Do something like option #2 but call a js function that shows a prompt and depending on the user response submits the form or navigates to a new page.

# re: ASP.NET MVC – Multiple buttons in the same form

Friday, August 5, 2011 6:56 PM by alexey.smolyakov

It's possible to always use only links. This is very simple approach, only jQuery must be referenced (actually easy to rewrite this to not use jQuery and/or move this onclick script to script file):

@Html.ActionLink("Save", "SaveActionName", null, null, new { onclick = "$(this).parents('form').attr('action', $(this).attr('href'));$(this).parents('form').submit();return false;" })

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, September 13, 2011 2:37 PM by Sergio

Awesome article!

Very useful for people who come from ASP.NET

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, January 30, 2012 3:23 AM by Robert

I'm think the solution was not sutiate with multiple languange. the button value will be difference for each language

I'm think we should check the button name value in request. if the "request[buttonname]!=null", it should be clicked by the button

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, January 31, 2012 4:46 PM by Alex

Worked perfectly! Thanks for sharing. You saved my day.

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, April 5, 2012 4:33 PM by Florence Tissot

Steven Bey, i like your solution.  you could modify it to allow the attribute to accept the name of the button (or buttons) that the action should react to, rather than having it take a prefix.  This would eliminate the need for a prefix.

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, April 5, 2012 5:02 PM by findleyd

Are you talking about the action method prefix (e.g. Register_Cancel)? Technically I think you can leave the _Cancel off of the name. I just did that for clarity.

# re: ASP.NET MVC – Multiple buttons in the same form

Tuesday, June 26, 2012 6:33 PM by TK

Awesome. Just what I was looking for and it works great just don't name the ActionResults the same as the Get method and you're laughing.

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, August 30, 2012 7:33 AM by Ashutosh

Greate article, Really very helpful

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, October 4, 2012 2:39 AM by Ankesh

Its working with IE 9. Not working with IE 7/8. getting null value for button in IE7/8

Please help.

# re: ASP.NET MVC – Multiple buttons in the same form

Thursday, October 25, 2012 5:12 PM by frustrated

great solution but I have 2 edit methods, one for post and 1 for get. I use data annotations for validation. Everything works first time but if validation fails, the GET method is called next time I press one of the buttons.

# re: ASP.NET MVC – Multiple buttons in the same form

Monday, October 29, 2012 11:35 AM by Ben

Like the solution but have an issue with IE8 when capatibility mode is turned on.  Would anyone know why it is an issue?