Monday, October 7, 2013

Handling multiple submit buttons in MVC

In this post I want to show you how to use multiple submit buttons on a single form and how to handle their requests in a controller.

This kind of scenario might be interesting when you need to process form data in two or more different ways. If you, for example, create a post you would probably expect to have two buttons once finished writing it. The first one is to preview your post and the second one is to publish it. Technically you would end up creating a view with a couple of input fields and two submit buttons. The problem with that approach is that the MVC framework can only link one controller action method to an html form out of the box.

However, you can work around this limitation by creating a custom ActionNameSelector attribute. The idea is to mark submit buttons with the names of controller action methods they are related to and to utilize those names while searching for an appropriate action method on the server side.

Here is my view for creating a post:

<!DOCTYPE html>
<html>
<body>
  @using (Html.BeginForm())
  {
    <div>
      Post title:
      @Html.TextBox("title")
    </div>
            
    if (ViewBag.NotificationMessage != null)
    {
    <div>
      @ViewBag.NotificationMessage
    </div>
    }

    <button type="submit" name="action" value="Save">Save my post</button>
    <button type="submit" name="action" value="Publish">Publish my post</button>
  }
</body>
</html>

As you can see, the buttons' value attributes have been set to the names of controller action methods. As next, we create a custom action name selector attribute which is able to pick an appropriate action method.

public class SubmitActionAttribute : ActionNameSelectorAttribute
{
  public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
  {
    HttpRequestBase request = controllerContext.RequestContext.HttpContext.Request;
    string action = request["action"];

    return methodInfo.Name.Equals(action, StringComparison.InvariantCultureIgnoreCase);
  }
}

And finally, we implement a controller with Save and Publish methods, which are tagged with the previously created attribute.

public class PostController : Controller
{
  public ActionResult Create()
  {
    // this is an entry point to the creation form
    return View();
  }

  [SubmitAction]
  [HttpPost]
  public ActionResult Save(string title)
  {
    ViewBag.NotificationMessage = string.Format("Post {0} has been saved", title);

    return View("Create");
  }

  [SubmitAction]
  [HttpPost]
  public ActionResult Publish(string title)
  {
    ViewBag.NotificationMessage = string.Format("Post {0} has been published", title);

    return View("Create");
  }
}

Voila! Now you are able to handle multiple submit requests inside your MVC controller.

No comments:

Post a Comment