Four Ways of Handling Multiple Submit Buttons in ASP.NET MVC
A common beginner question is - How to handle multiple submit buttons in
ASP.NET MVC?
In web forms this is quite straightforward because all you need to do is
write the Click event handler of the respective buttons. However, in ASP.NET MVC
you need to rely on the plain HTML and / or JavaScript to deal with the
situation. To that end this article illustrates four ways of handling multiple
submit buttons.
The following code uses a simple Customer model class that looks like this:
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string Country { get; set; }
}
The Index view accepts values for all the properties of the Customer classly
CustomerID, CompanyName, ContactName and Country.
1. Multiple buttons with different names
In this technique you use BeginForm() helper as usual and submit a form to an
action method. The relevant markup from the Index view that does this is shown
below:
@model MultipleSubmitButtons.Models.Customer
...
...
@using (Html.BeginForm("ProcessForm", "Home", FormMethod.Post))
{
@Html.EditorForModel()
<br />
<input type="submit" name="save" value="Save" />
<input type="submit" name="cancel" value="Cancel" />
}
As you can see the BeginForm() helper specifies the action name to be
ProcessForm, controller name to be Home and the form method to be POST. To
display a data entry textboxes EditorForModel() helper is used. You can very
well use helpers such as TextBoxFor() and LabelFor() if you so wish.
There are two submit buttons - one with name attribute set to save and the
other with name of cancel. When you click a particular button, its name and
value is sent to the server. The ProcessForm() action needs to grab these values
to detect which one was clicked.
This is how the Index view looks like in the browser:
Th ProcessForm() action that does this detection is shown below:
[HttpPost]
public ActionResult ProcessForm(Customer obj,
string save,string cancel)
{
if(!string.IsNullOrEmpty(save))
{
ViewBag.Message = "Customer saved successfully!";
}
if (!string.IsNullOrEmpty(cancel))
{
ViewBag.Message = "The operation was cancelled!";
}
return View("Result",obj);
}
Notice that the ProcessForm() action receives three parameters through model
binding. The first parameter is the Customer object that holds the values
entered in the textboxes. The save and cancel string parameters hold the value
of the respective buttons. These parameter names must match the names of the
butttons on the form. If a button is clicked, its value is received in the
action. Otherwise its value will be null. For example, if you click on the Save
button the save parameter will hold Save and the cancel parameter will hold
null.
The if blocks then check the value of save and cancel parameters and
accordingly set a ViewBag property. The ProcessForm() action returns Result
view. The markup of Result view is shown below:
@model MultipleSubmitButtons.Models.Customer
...
<body>
<h1>@ViewBag.Message</h1>
@Html.DisplayForModel()
</body>
</html>
The Result view simply displays the Message property from the ViewBag and
also displays the value held in the Customer object.
2. Multiple buttons with the same name
This technique is similar to the previous technique. But the buttons involved
are given the same name in the HTML markup.
@using (Html.BeginForm("ProcessForm", "Home", FormMethod.Post))
{
@Html.EditorForModel()
<br />
<input type="submit" name="submit" value="Save" />
<input type="submit" name="submit" value="Cancel" />
}
Notice that the name attribute of both the buttons is set to submit and their
value attribute is set to some string.
Then the ProcessForm action accepts a single parameter - submit - that
receives the value of the button clicked by the user. This is shown below:
[HttpPost]
public ActionResult ProcessForm(Customer obj, string submit)
{
switch(submit)
{
case "Save":
ViewBag.Message = "Customer saved successfully!";
break;
case "Cancel":
ViewBag.Message = "The operation was cancelled!";
break;
}
return View("Result", obj);
}
As you can see the submit parameter is checked for its value. If you click
the Save button, submit parameter will be Save. If you click on Cancel button
the submit parameter will have alue of Cancel. The switch statement then checks
the values and does some processing specific to a button.
3. HTML5 formaction and formmethod attributes
In this technique you use the formaction and formmethod attributes introduced
in HTML5. These attributes can be set on the buttons under consideration. The
formaction attribute indicates the form's action whereas the formpost attribute
indicates the form submition method.
The modified form markup is shown below:
@using (Html.BeginForm())
{
@Html.EditorForModel()
<br />
<input type="submit" name="save" value="Save"
formaction="SaveForm" formmethod="post" />
<input type="submit" name="cancel" value="Cancel"
formaction="CancelForm" formmethod="post" />
}
As you can see the formaction attribute of the save button submits to SaveForm() action whereas the
cancel button submits to the CancelForm() action. The formmethod attribute is
set to post for both the buttons.
The SaveForm() and CancelForm() actions are shown below:
[HttpPost]
public ActionResult SaveForm(Customer obj)
{
ViewBag.Message = "Customer saved successfully!";
return View("Result", obj);
}
[HttpPost]
public ActionResult CancelForm(Customer obj)
{
ViewBag.Message = "The operation was cancelled!";
return View("Result", obj);
}
The SaveForm() needs to accept only the Customer model object because this is
exclusively called by the save button. Similarly the CancelForm() action needs
to receive only the model object since it is invoked exclusively by the cancel
button.
4. jQuery / JavaScript code
If above techniques doesn't meet your requirements you can
always use jQuery or JavaScript to programatically set the form action. The
following markup shows how the modified form looks like:
@using (Html.BeginForm())
{
@Html.EditorForModel()
<br />
<input type="submit" id="save" value="Save" />
<input type="submit" id="cancel" value="Cancel" />
}
This time the ID attribute of the buttons is set to save and cancel
respectively. This way you can easily access them in the jQuery code. The jQuery
code that wires the click event handlers for these buttons and sets the form
action dynamically is shown below:
$(document).ready(function () {
$("#save").click(function () {
$("form").attr("action", "/home/saveform");
});
$("#cancel").click(function () {
$("form").attr("action", "/home/cancelform");
});
});
The click event handler of the save button sets the action attribute of the
form to /home/saveform. This is done using the attr() method of jQuery. On the
same lines the click event handler of the cancel button sets the action to
/home/cancelform.
Which technique to use?
All the techniques discussed above are standard ways of dealing with multiple
submit buttons. So, the selection depends on developers choice and application
requirements. If your logic is such that a single action is doing multiple
things then technique #1 comes handy. An example of this technique can be seen
here that develops a
wizard with next and previous buttons. The second technique has a drawback that
if you change the value attribute then you need to adjust the switch statement
accordingly. Moreover, it may pose a problem in multilingual websites since you
are checking the value displayed on the buttons. So, this technique should be
avoided if you anticipate the button texts to change. If the target browser supports HTML5 (all
the leading browsers support these attributes) then using formaction and
formmethod attributes is recommended. This way you get a cleaner separatation of
the concerns and the code is more readable. If the form or the buttons
themselves are being generated on-the-fly then jQuery technique is handy because
things are changing dynamically.