14

Consider the following markup:

<h2>Edit SAS Program</h2>
@using (Html.BeginForm("Edit", "SasProgram", FormMethod.Post))
{
    <label for="Name">Name</label>
    @Html.TextBoxFor(model => model.Name)

    using (Html.BeginForm("Delete", "SasProgram", FormMethod.Post))
    {
        <input type="submit" class="button" value="Delete" />
    }
    <input type="submit" class="button" value="Save Changes" />
}

I'd like to have the Delete button on the same view as the Edit. However, it's not letting me have nested forms. What is the appropriate way to handle this situation?

I tried leveraging this answer, How to handle nested forms in ASP.NET MVC, but it's a broken link now.

Community
  • 1
  • 1
Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232

8 Answers8

17

I would use different values for button name in the same form:

@using (Html.BeginForm("Edit", "SasProgram", FormMethod.Post))
{
    <label for="Name">Name</label>
    @Html.TextBoxFor(model => model.Name)

    <button name="action" value="delete">Delete</button>
    <button name="action" value="save">Save Changes</button>
}

and then switch in controller:

[HttpPost]
public ActionResult Edit( SomeModel model, string action )
{
    switch( action ) {
        case "delete":
            // delete action
            break;
        case "save":
            // save action
            break;
    }
}

The code is written from memory but it works in production. Note that buttons are of default type - submit.

nrodic
  • 3,026
  • 3
  • 33
  • 38
15

The best and easiest way would be to use two forms but don't nest them:

<h2>Edit SAS Program</h2>
@using (Html.BeginForm("Edit", "SasProgram", FormMethod.Post))
{
    <label for="Name">Name</label>
    @Html.TextBoxFor(model => model.Name)

    <input type="submit" class="button" value="Save Changes" />
}

@using (Html.BeginForm("Delete", "SasProgram", FormMethod.Post))
{
    <input type="submit" class="button" value="Delete" />
}

This way you have:

  • Two separate forms
  • No GET requests
  • The delete button below the edit button, which makes more sense when you're on a view that allows you to edit something.
Kristof Claes
  • 10,797
  • 3
  • 30
  • 42
2

First of all. Every modification request should be use the post method.
I make some R&D and build the basics of a multi submit button handler in a wiki A clean solution to use multiple submit button in ASP.NET MVC.
I think it could solve your problem.

Community
  • 1
  • 1
Péter
  • 2,161
  • 3
  • 21
  • 30
1

First, you cannot nest <form> element. The specification doesn't allow it. Since you are using the MVC pattern I have two options that came to my mind:

  1. You can retain the save button as the submit button of the form, and make the delete button a HTML link. Then the delete button will target to a different route, it could be something like: GET /program/delete/{id}.

  2. You can have two buttons inside the same form, then with JavaScript after clicking one of the buttons you will change the action attribute of the form.

Update

There is a third option, that is more clean: using two submit buttons with same name attribute and different values.

Your form will have two buttons:

public ActionResult MyAction(string submitButton) {
    switch (submitButton) {
        case "save":
            // ...
        case "delete":
            // ...
    }
}

For more details check this answer: https://stackoverflow.com/a/443047/439427

Community
  • 1
  • 1
Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
1

You can also use html 5 feature to target a form from an input button. Below I have created both a delete and save form and have the submit buttons outside of the forms but targeting them via the form attribute.

I think most browsers support this except IE.

No javascript required.

@using (Html.BeginForm("Edit", "SasProgram", FormMethod.Post, new { id = "editForm" }))
{
    <label for="Name">Name</label>
    @Html.TextBoxFor(model => model.Name)
}

@using (Html.BeginForm("Delete", "SasProgram", FormMethod.Post, new { id = "deleteForm" }))
{
    <input type="submit" class="button" value="Delete" />
}

<input type="submit" class="button" value="Save" form="editForm"/>
<input type="submit" class="button" value="Delete" form="deleteForm" />

This allows for a nice button layout without any fancy javascript or css styling.

Ramon Smits
  • 2,482
  • 1
  • 18
  • 20
0

The OLD way to do this but still applicable is to have one form tag and change the action with multiple submit buttons.

<input class="btn btn-default" type="submit" value="Save" />
<input class="btn btn-default" type="submit" value="Delete" onclick="this.form.action='/SasProgram/delete/@Model.Id';" />
0

Hi dear friend you can check out this solution: https://www.dotnettricks.com/learn/mvc/handling-multiple-submit-buttons-on-the-same-form-mvc-razor

enter image description here

@using (Html.BeginForm("MultipleCommand", "Home", FormMethod.Post, new { id = "submitForm" }))
{
 <fieldset>
 <legend>Registration Form</legend>
 <ol>
 <li>
 @Html.LabelFor(m => m.Name)
 @Html.TextBoxFor(m => m.Name, new { maxlength = 50 })
 @Html.ValidationMessageFor(m => m.Name)
 </li>
 <li>
 @Html.LabelFor(m => m.Address)
 @Html.TextAreaFor(m => m.Address, new { maxlength = 200 })
 @Html.ValidationMessageFor(m => m.Address)
 </li>
 <li>
 @Html.LabelFor(m => m.MobileNo)
 @Html.TextBoxFor(m => m.MobileNo, new { maxlength = 10 })
 @Html.ValidationMessageFor(m => m.MobileNo)
 </li>
 </ol>
 <button type="submit" id="btnSave" name="Command" value="Save">Save</button>
 <button type="submit" id="btnSubmit" name="Command" value="Submit">Submit</button> 
 <button type="submit" id="btnCancel" name="Command" value="Cancel" onclick="$('#submitForm').submit()">Cancel (Server Side)</button>
 </fieldset>
} 

Murat KA
  • 137
  • 1
  • 7
  • Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. **Would you kindly [edit] your answer to include additional details for the benefit of the community?** – Jeremy Caney May 17 '23 at 00:21
-1

Edit : Here's how to do it with ajax using an HttpPost.

//
// POST: /Divisions/Delete
[HttpPost, ActionName("Delete"), Authorize]
public ActionResult DeleteConfirmed(int id)
{
    Division division = _db.Divisions.Single(x => x.DivisionId == id);

    string errorMessage;
    if (DbRelationEnforcer.CanDelete(_db, division, out errorMessage))
    {
        division.SetDeleted(User.Identity.Name);
        _db.SaveChanges();
        return Json(new JsonResponseCreatePartial { Success = true }, JsonRequestBehavior.AllowGet);
    }

    return Json(new JsonResponseCreatePartial { Success = false, Message = errorMessage }, JsonRequestBehavior.AllowGet);
}

Then, on the view, you must use the <input type="submit">Save changes</input> to save your changes (within the form), and a simple link/button to delete, like this:

<h2>Edit SAS Program</h2>
@using (Html.BeginForm("Edit", "SasProgram", FormMethod.Post))
{
    <label for="Name">Name</label>
    @Html.TextBoxFor(model => model.Name)

    <input id='delete-btn' type="button" class="button" value="Delete" />
    <input type="submit" class="button" value="Save Changes" />
}

Finally, you have to use JS to post to your action from the view, when the user clicks on Delete.

<script type='text/javascript'>
    $(function() {
        $("input#delete-btn").click(function(){
            $.post('@Url.Action("Delete")', '@Model.Id', function(data) {
                if(data.Success) {
                    ' ... handle the success case
                } else {
                    ' ... error management
                }
            });
        });
    });
</script>

This will work, but in order to have a better UX, it would be preferable to have the Delete button from the Index/list view, and using a JQuery UI dialog to confirm before doing the ajax post. This will skip having to load the Edit page if/when you want to delete multiple items one after the other.

Matthew Perron
  • 6,201
  • 2
  • 21
  • 24
  • 1
    Doing deletes via hackable and indexable get requests? That's just very bad advice! – Kristof Claes Jul 15 '13 at 20:19
  • 1. The controller has an [Authorize] attribute, 2. As I mentionned in my edit, this "works" but it's much better to use a confirmation dialog and do an ajax post once the user has confirmed. 3. The form url is set from javascript so it shouldn't be indexable. – Matthew Perron Jul 15 '13 at 20:25
  • 2
    @KristofClaes not to mention that several browsers have pre-fetch; view the parent? boom: all the children are deleted – Marc Gravell Jul 15 '13 at 20:31
  • @MarcGravell Changed the answer slightly to use an ajax post. IMO this is better than passing an action identifier in the form and routing everything within the same MVC Action on the controller side of things... Using the same MVC Action to save changes or to delete is anti-cohesive. – Matthew Perron Jul 15 '13 at 21:37