charlie_cat
charlie_cat

Reputation: 1850

Best way to handle 2 submit buttons for one Html.BeginForm

I have a Html.BeginForm in a view that contains details for a user: firstname, surname, email etc. Then I have 2 buttons. Approve and Reject

When approve is clicked I go to one view.

When reject, I go to another.

What would be the best way to handle which one was clicked?

in my View:

<% using (Html.BeginForm("PublishGroupsForRecommendedUser", "Recommend", FormMethod.Post, new { id = ViewBag.UserId }))
  { %>
   <div class="section _100">
    <%: Html.LabelFor(model => model.Email)%> 
    <div>                                      
      <%: Html.EditorFor(model => model.Email)%>  
      <%: Html.ValidationMessageFor(model => model.Email)%>    
    </div>
   </div>            

   //etc

 <input type="hidden" name="action">
 <div class="actions">
  <div class="actions-right">
    <input type="submit" value="Approve" class="submit_button" />
  </div>
  <div class="actions-left"> 
    <input type="submit" value="Reject" class="submit_button" />
  </div>
  </div>
  <% } %>

In my Controller:

   [HttpPost]
  public ActionResult PublishGroupsForRecommendedUser(int userId)
  {
    var recommendedUser = ZincService.UserService.GetRecommendedUserForId(userId);
    var visibleGroups = ZincContext.CurrentUserGroups.Get();
    var groupEntities = ZincService.GroupService.GetVisibleGroupsForUser(CurrentUser.UserId).ToList();

    var viewModel = GetGroupPublishingViewModelForSelectedGroups(
    recommendedUser.RecommendedUserId, Zinc.Enums.PublishingMode.ParticipatingGroupUsersOnly,
    recommendedUser.Email, groupEntities);

    return View(viewModel);
  }


  [HttpGet]
  public ActionResult RejectUser(RecommendUserViewModel model)
  {
    Entities.RecommendedUser user = new RecommendedUser();
    user.ReasonForRejection = model.ReasonForRejection;
    ZincService.UserService.UpdateRecommendedUser(user);
    return View(user);
  }

So I cannot use the line using (Html.BeginForm("PublishGroupsForRecommendedUser", "Recommend", FormMethod.Post, new { id = ViewBag.UserId })) anymore because depending on which button was clicked I need to go to the PublishGroupsForRecommendedUser or RejectUser action which in turn will call the corresponding View.

Can someone recommend me the best way?

Upvotes: 1

Views: 3313

Answers (3)

charlie_cat
charlie_cat

Reputation: 1850

in my view:

<asp:Content ID="Content2" ContentPlaceHolderID="ScriptPlaceHolder" runat="server">

<script type="text/javascript">
 function RejectUser(userId) {
   $.ajax({
     url: '<%= Url.Action("RejectUser", "Recommend") %>',
     type: 'GET',
     dataType: 'json',
     data: { id: userId, reasonForRejection: $('#textarea').val() },
     success: function (data) {
       window.location.href = data.redirectTo;
     }
    });
 }
</script>

</asp:Content>

<div class="actions">
   <div class="actions-right">
      <input type="submit" value="Approve" class="submit_button" />
   </div>
   <div class="actions-left">      
      <a href="javascript:RejectUser(<%: Model.RecommendedUserId %>);" class="button" id="submit_button">Reject</a>
   </div>
</div>

and in the controller:

   [HttpGet]
  public JsonResult RejectUser(int id, string reasonForRejection)
  {
    if (!String.IsNullOrWhiteSpace(reasonForRejection))
    {
      Entities.RecommendedUser user = new RecommendedUser();
      user = ZincService.UserService.GetRecommendedUserForId(id);
      user.ReasonForRejection = reasonForRejection;
      ZincService.UserService.UpdateRecommendedUser(user);
      ZincService.SaveChanges();
    }
    return Json(new
    {
      redirectTo = Url.Action("RecommendedUsers"),
    }, JsonRequestBehavior.AllowGet);
  }

thanks all!

Upvotes: 0

shakib
shakib

Reputation: 5469

If you use jquery, one solution could be,

Give the form an id, and set the action url for both submit button in the element as data attribute.

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "my-form" }))
{
    //form elements

    <input type="submit" name="action" value="Approve" data-action="@Url.Action("Approve", "YourController")" />
    <input type="submit" name="action" value="Reject" data-action="@Url.Action("Reject", "YourController")"/>
}

and then attach the submit buttons click event with jquery and aattch the action to the form.

$(function () {
    $('#my-form :submit').click(function (e) {
        var button = $(this);
        button.closest('form').attr('action', button.attr('data-action'));
    });
});

hope this helps.

Upvotes: 0

vinczemarton
vinczemarton

Reputation: 8156

I'm not entirely sure what you want, but I think a little restructuring would help your code:

ASP.NET-MVC makes it easy to handle input from forms by using a specific ViewModel for your view. Make a property for everything you want to post back:

You make a simple POCO object, like:

public class Person {
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsApproved { get; set; }
}

The ID is in a much better place on your Model than it is in ViewBag. It is a part of the model, don't be afraid to put it there. It will also be filled on getting the results of the post. Quite conveniently.

Let's say your first action is:

public ActionResult PersonForm()
{
    var model = new Person()
    {
        ID = WhateverYouWant()
    };

    return this.View(model);
}

You can use it as a model from your View:

<%@ Page Title="Anything" Language="C#" Inherits="System.Web.Mvc.ViewPage<MVC3ASPX.Models.Person>"... %>
...
...
<% using (Html.BeginForm())
   { %>
<%: Html.ValidationSummary(true) %>
<%: Html.HiddenFor(model => model.ID)%>
<div>
    <%: Html.LabelFor(model => model.Name)%>
    <div>
        <%: Html.EditorFor(model => model.Name)%>
        <%: Html.ValidationMessageFor(model => model.Name)%>
    </div>
</div>
<div>
    <%: Html.LabelFor(model => model.Age)%>
    <div>
        <%: Html.EditorFor(model => model.Age)%>
        <%: Html.ValidationMessageFor(model => model.Age)%>
    </div>
</div>
<% } %>

Notice how I made a hidden input field for the ID. It will get posted back.

Also notice that I didn't specify the method (get or post). Post is the default, it will suffice for our needs. I also didn't specify WHERE to post it. By default, the form will post back to the url it is on. In our case this will be the action PersonForm.

Posting 2 ways is not the best practice in ASP.NET. Post to one action and make an if there to decide what to do.

So make 2 buttons. <button>s are more flexible than <input>s for submitting something, as they can have different text and value.

<div class="actions">
    <div class="actions-right">
        <button type="submit" name="IsApproved" value="True" class="submit_button">Approve</button>
    </div>
    <div class="actions-left">
        <button type="submit" name="IsApproved" value="False" class="submit_button">Reject</button>
    </div>
</div>

Note that the buttons will have the text "Approve" and "Reject", but the value posted back will be True or False, depending where you clicked.

Your action handling the post should look like:

    [HttpPost]
    public ActionResult PersonForm(Person model)
    {
        if (this.ModelState.IsValid) // Validation never hurts
        {
            if (model.IsApproved)
            {
                return this.PersonFormApproved(model); // Your first method goes here
            }
            else
            {
                return this.PersonFormRejected(model); // Your second goes here
            }
        }

        return this.View(model); // If the model's state is invalid, let's try this one more time!
    }

In the model variable you will have every property filled from the values of your form. Also since there is a property called IsApproved, it will get filled by the form element of the same name. The button. And only the pressed one.

Notice that I have extracted most of the inner logic to methods: PersonFormApproved and PersonFormRejected. These should be private to avoid accidental calls by the program mistakenly thinking they are callable actions.

They should return ActionResult though, since PersonForm action will return their results.

Also check ModelState.IsValid. Only handle the info if it is valid. Check out DataAnnotations on how do you want your model to be validated.

Upvotes: 3

Related Questions