1

I have a form where I fill in a blank and then save it, which takes me to the read only version of the entry I've just saved. At this step I have 2 options: Either "Confirm" or "Cancel". Both actions will update some values in the database, so they're both good candidates for submit button(HttpPost, of course). I know this shouldn't be tied to a particular web development technology as all-in-all it's all about Request and Response, but still I'll talk in ASP.NET MVC "language" as that is what I'm using now. So far, the only idea that has come to my mind is to have a separate submit buttons with different names and on the server side check for the name and act accordingly. But this seems a bit ugly to me as I might have to have a huge and universal action method. Do you have any better approach? Maybe MVC has some built in way for this purpose?

Mikayil Abdullayev
  • 12,117
  • 26
  • 122
  • 206

3 Answers3

3

huge and universal action method

That's entirely your responsibility. You write action methods to map HTTP requests to business logic.

public class PostModel<T>
{
    public string SubmitAction { get; set; } 
    public T TheModel { get; set; }
}

public ActionResult HandleSomePost(PostModel<Foo> model)
{
    switch (model.SubmitAction.ToUpperInvariant())
    {
        case "SAVE":
            _yourBLL.Save(model.TheModel);
            break;
        case "CANCEL":
            _yourBLL.Cancel(model.TheModel);
            break;
        default:
            throw new ArgumentException("SubmitAction");
    }

    return View();
}

Then you can handle both submit buttons from this form:

@using (Html.BeginForm("HandleSomePost", "TheController", FormMethod.Post))
{
    @Html.EditorFor(m => m.TheModel)
    <input type="submit" name="SubmitAction" value="Cancel">
    <input type="submit" name="SubmitAction" value="Save">
}

However, this will quickly become a mess. You can also use attributes to let submit buttons map to action methods (though for simplicity I removed the "action:" prefix used for localization, be sure to read that Q&A and the linked blog):

[HttpPost]
[MultipleSubmit(SubmitAction = "Save")]
public ActionResult Save(Foo model) 
{
    _yourBLL.Save(model);
    return View();
}

[HttpPost]
[MultipleSubmit(SubmitAction = "Cancel")]
public ActionResult Cancel(Foo model)
{
    _yourBLL.Cancel(model);
    return View();
}
Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Also a nice approach. – Mikayil Abdullayev Apr 09 '15 at 08:17
  • Are there any downsides to using the `[MultipleSubmit]` attribute? This seems very efficient compared to any other alternative . I would put that answer on top and the switch case below. –  Apr 09 '15 at 08:33
  • 2
    @gerdi I like to work up my answers to the (arguably) "best" solution, so one can read it top-down. :) – CodeCaster Apr 09 '15 at 08:36
  • @CodeCaster Yeah , that makes sense , it probably helps users learn a larger scope to a solution. Cheers –  Apr 09 '15 at 08:38
2

You could do this with 2 forms, each posting to different action methods:

<form method="post" action="/cancelAction">
    <input name="id" type="hidden" value="some-id-value">
    <input type="submit" value="Cancel">
</form>

<form method="post" action="/confirmAction">
    <input name="id" type="hidden" value="some-id-value">
    <input type="submit" value="Confirm">
</form>

Or using MVC Razor syntax since you mentioned it:

@using (Html.BeginForm("cancelAction", "MyController", FormMethod.Post))
{
    @Html.HiddenFor(model => model.ID)
    <input type="submit" value="Cancel">
}

@using (Html.BeginForm("confirmAction", "MyController", FormMethod.Post))
{
    @Html.HiddenFor(model => model.ID)
    <input type="submit" value="Confirm">
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • 1
    Suggest you include `@Html.AntiForgeryToken()` also and the ID could be added as a route value instead of a hidden input (note the manual html wont work unless you add a `name` attribute to the input) –  Apr 09 '15 at 08:15
  • @StephenMuecke Good point on the `name` attribute. Will leave the anti forgery token to the user to decide but I agree it's a good idea. – DavidG Apr 09 '15 at 08:18
2

From the information above it seems hard to pin down the exact use case here, however it might not be neccessary to have both a post for the confirm and cancel.

Consider using the submit event for "confirmation" and then just call the cancel event using normal HTTP-GET and passing the item that needs to be cancelled's ID? Then you can either handle the confirm or cancel events in either of the Actions directly or do a RedirectToAction to the HugeUniversalAction.

@using (Html.BeginForm("Confirm","ExampleController",FormMethod.Post))
{
    <input type/>
    <input type="submit" value="confirm"/>
    @Html.ActionLink("Cancel", "ExampleController", id = Model.Id)

}

Then in your controller you can call the larger universal method.

 public ActionResult Cancel(int id)
    {
        // Cancel directly
        // or 
          return RedirectToAction("HugeUniversalAction", new { confirm = "false", id = id });

    }

    [HttpPost]
    public ActionResult Confirm(Foo model)
    {
       // Confirm Directly
       // or 
       return RedirectToAction("HugeUniversalAction", new { confirm = "true", id = model.Id });
    }

Then handle the two paths in whichever way you need to in your HugeUniversalAction

public ActionResult HugeUniversalAction(int id, confirm = false){ // If confirm confirm it else cancel it }
Auronmatrix
  • 81
  • 1
  • 5