utorak, 2. veljače 2010.

Multiple submit buttons on ASP.NET MVC forms


I am currently developing a medium CMS site, and chose to try out ASP.NET MVC, powered by sharp architecture. Before I get to the point, I must say that I am allways amased with any tools that reduce manual and repeating, boring, every-app-the-same coding to minimum, but usually my enthusiasm plumps when I realize that I actually have to write repeating code all the time! Still, with this one I am quite satisfied.

Now, actually getting to a point: what to do when You have to have multiple buttons on a same form and, hopefully, being able to "catch appropriate events" in controller. Here's what I came up with:

Let's start with controller, in which we have two functions, both are actions available in a view "Edit". In a controller, we want to have functions like these:

public ActionResult Filter(string filterString){ ... }

public ActionResult Save(FormCollection form) { ... }

First of all, they are called from a same view, so basically they should have been called the same, "Edit", but luckily, we have an attribute to assign them a real name:

[ActionName("Edit") ]

Now, we still have to tell a submit button which function to "call". It is not actually possible like that, but we can add a parameter to button, or, value:

<button name="button" value="someValue" type="submit">

Next, we somehow have to allow for a controller action to be called only for button we want, thats where this small piece of code jumps in:

public class AcceptSubmitButton : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest
(ControllerContext controllerContext,MethodInfo methodInfo)
{
var req = controllerContext.RequestContext.HttpContext.Request;
return req.Form["button"] == methodInfo.Name;
}
}

And our controller methods should look like:

[ActionName("Edit") , AcceptSubmitButton]
public ActionResult Filter(string filterString){ ... }

[ActionName("Edit") , AcceptSubmitButton]
public ActionResult Save(FormCollection form) { ... }

The problem with this solution is that each time we create a submit button in a view, we have to set name attribute to "button", and value to controller method name (not action name). To take cate that this is done well, we introduce Html extension (namespace is Microsoft.Web.Mvc on purpose so it is included in every view automatically):

namespace Microsoft.Web.Mvc
{
public static class HtmlExtensions
{
public static string ActionSubmitButton(this HtmlHelper helper,string text, string actionName)
{
return "<button name="\" value="\">
+ "\" type=\"submit\">" + text
+ "</button>";
}
}
}
In view, rendering this button looks something like:

<%= Html.ActionSubmitButton("Perform filter", "Filter") %>
<%= Html.ActionSubmitButton("Perform save", "Save") %>

for Filter controller method, and Save method respectively.