27

Right now I understand

if (IsPost){   //do stuff }

checks all post methods on that page. However, I have 2 different forms posting 2 different information. These are a login form and a register form.

Is there a way I can check IsPost based on which form? For example,

if(Login.IsPost){ //do stuff }

but how would I define the Login variable? My form looks like:

<form id="Login" method = "POST">

I have tried:

var Login = Form.["Login"]

it did not work.

I will appreciate any help.

Thanks.

Nathan Wakefield
  • 105
  • 1
  • 11
Davidred15
  • 273
  • 1
  • 3
  • 5
  • I answered a similar question. I dont want to copy paste whole solution. [Your solution might be here.](http://stackoverflow.com/questions/822751/multiple-forms-in-asp-net-mvc/28525230#28525230) – hakan Feb 15 '15 at 10:43

5 Answers5

38

In an MVC view, you can have as many forms with as many fields as you need. To keep it simple, use a single view model with all the properties you need on the page for every form. Keep in mind that you will only have access to the form field data from the form that you submit. So, if you have a login form and registration form on the same page you would do it like this:

LoginRegisterViewModel.cs

public class LoginRegisterViewModel {
    public string LoginUsername { get; set; }
    public string LoginPassword { get; set; }

    public string RegisterUsername { get; set; }
    public string RegisterPassword { get; set; }
    public string RegisterFirstName { get; set; }
    public string RegisterLastName { get; set; }
}

YourViewName.cshtml

@model LoginRegisterViewModel

@using (Html.BeginForm("Login", "Member", FormMethod.Post, new {})) {

    @Html.LabelFor(m => m.LoginUsername)
    @Html.TextBoxFor(m => m.LoginUsername)

    @Html.LabelFor(m => m.LoginPassword)
    @Html.TextBoxFor(m => m.LoginPassword)

    <input type='Submit' value='Login' />

}

@using (Html.BeginForm("Register", "Member", FormMethod.Post, new {})) {

    @Html.LabelFor(m => m.RegisterFirstName)
    @Html.TextBoxFor(m => m.RegisterFirstName)

    @Html.LabelFor(m => m.RegisterLastName)
    @Html.TextBoxFor(m => m.RegisterLastName)

    @Html.LabelFor(m => m.RegisterUsername)
    @Html.TextBoxFor(m => m.RegisterUsername)

    @Html.LabelFor(m => m.RegisterPassword)
    @Html.TextBoxFor(m => m.RegisterPassword)

    <input type='Submit' value='Register' />

}

MemberController.cs

[HttpGet]
public ActionResult LoginRegister() {
     LoginRegisterViewModel model = new LoginRegisterViewModel();
     return view("LoginRegister", model);
}

[HttpPost]
public ActionResult Login(LoginRegisterViewModel model) {
 //do your login code here
}

[HttpPost]
public ActionResult Register(LoginRegisterViewModel model) {
 //do your registration code here
}

Do not forget, when calling BeginForm, you pass the Controller name without "Controller" attached:

@using (Html.BeginForm("Login", "Member", FormMethod.Post, new {}))

instead of:

@using (Html.BeginForm("Login", "MemberController", FormMethod.Post, new {}))
Nathan Wakefield
  • 105
  • 1
  • 11
jpshook
  • 4,834
  • 6
  • 36
  • 45
  • 33
    How you do validation then? Let's say you have model decorated with Required attributes and add 2 validation summaries, but then how you make sure, that only Login fields are included in first summary and only register fields are in second? And as well that by pressing Login you will validate only Login fields? – Evaldas Dzimanavicius Nov 19 '13 at 10:21
  • 5
    this ain't a good approach. you need 1 modelbinding for each form, all on same viewmodel and prefix the controller with the name of the property of each model – Bart Calixto Dec 01 '13 at 17:27
  • 4
    This is half way right, half way wrong. How would you validate your model? How can this even be an accepted answer? Unbelievable. – Pepito Fernandez Feb 21 '16 at 21:42
  • How will you validate using data annotation? – Wai Yan Hein Jul 09 '16 at 10:00
  • Question: Can you not make the viewmodel a parent class with two sub classes for each form? – Terrance00 Jul 18 '16 at 17:28
  • @jpshook i am dealing with the same scenario but the difference is i am using file upload in one of two forms , issue is the form without file upload is working, but other one is not posting data to controller action. i am not making foolish mistakes like syntax mistake etc. – Rizwan Ali Sabir Nov 24 '16 at 14:15
  • Which form gets submitted when you hit enter? The first? – Lawyerson Feb 06 '17 at 08:14
28

I'd just load a partial view (containing a form) for each needed form, giving each partial a different viewmodel:

  • Multiple-form-on-a-page-requirement: satisfied.
  • Javascript unobtrusive validation on each form: accomplished.
edtruant
  • 883
  • 8
  • 9
  • 1
    There is one problem with this method. Any server-side validations will get swallowed up. – Dylan Oct 06 '16 at 13:01
  • 2
    For those having issues with this solution, make sure you arent using the auto-generated `@using (Html.BeginForm())` on your partial view for the form submission wont work, you'll need to use the more explicit `@using (Html.BeginForm("Login", "Member", FormMethod.Post, new {}))` instead. Thanks +1 – Dan Beaulieu Nov 05 '16 at 18:42
4

Instead of doing a form Submit, we can do a ajax post on click of corresponding submit button.

@using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { @Id = "Form1" }))
{

}

@using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { @Id = "Form2" }))
{

}

Once you allocate different id attribute to each of the form in your page, use code like this:

$(document).ready( function() {
  var form = $('#Form1');

 $('#1stButton').click(function (event) {

    $.ajax( {
      type: "POST",
      url: form.attr( 'action' ),
      data: form.serialize(),
      success: function( response ) {
        console.log( response );
      }
    } );
  } );
}

Repeat the same thing on 2nd Button click event to invoke the ajax post for the 2nd form.

This code uses .serialize() to pull out the relevant data from the form.

For future reference, the jQuery docs are very, very good.

NB : The button you are using to trigger the event that causes the submit of the form via ajax post should not be of type submit! Else this will always fail.

Biki
  • 2,518
  • 8
  • 39
  • 53
3

You should make each <form> point to a separate action with its own model parameters.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • can i have 2 forms in the same view have 2 model parameters? – Davidred15 Apr 03 '13 at 13:38
  • I'm not sure what you mean. – SLaks Apr 03 '13 at 13:39
  • I have both form ins default.cshtml and IsPost checks that page however both form post differently, how would I make a different model on the same page? – Davidred15 Apr 03 '13 at 13:45
  • He's asking if one page can have two forms pointing to two different places with different viewmodels. – ametren Apr 03 '13 at 13:45
  • 1
    yes, unlike the WebForms world where only "one form was allowed" on a page, MVC allows you to have multiple forms on the page submitting to their own individual actions – Anthony Shaw Apr 03 '13 at 13:57
  • how can the viewmodel identify which form is posting? – Davidred15 Apr 03 '13 at 14:05
  • Either send to two different actions (preferable), so you'll know which form was posted based on which action is invoked, or you can do something old school like name your submit buttons uniquely and then check which name made it into the posted form data. – Chris Pratt Apr 03 '13 at 14:16
  • 1
    @user2238691: The ViewModel doesn't do anything. Each form should post to a different action method. – SLaks Apr 03 '13 at 17:52
0

Use this example to prevent unnecessary/unintended validation checks while using multiple Form Post with different Controller Actions.


What to look for

  • Essentially this code utilizes a Boolean inside of the Model to flag which Controller Action was called in the Form Post.
  • Notice the use of the nest model and the [Is Action Login] and [Is Action Register] Boolean properties that I set using the method helper IsActionLogin() and IsActionRegister(). Only one will be called in the respective Controller Action.
  • Notice the sReturnURL property in the model. This property stores previous navigation url and is shared with the both Login and Register Controller Actions. This will allow us to return to the page the user left off prior to having to log in.


    /* Begin Model logic */
    public class LoginRegisterModel
    {
        public LoginModel LoginModel { get; set; }
        public RegisterModel RegisterModel { get; set; }
        public string sReturnURL { get; set; }
        public bool bIsActionLogin { get; set; }
        public bool bIsActionRegister { get; set; }
        public void IsActionLogin()
        {
            bIsActionLogin = true;
            bIsActionRegister = false;
        }
        public void IsActionRegister()
        {
            bIsActionLogin = false;
            bIsActionRegister = true;
        }
    }
    
    public class LoginRegisterModel
    {
        public LoginModel LoginModel { get; set; }
        public RegisterModel RegisterModel { get; set; }
        public string sReturnURL { get; set; }
        public bool bIsActionLogin { get; set; }
        public bool bIsActionRegister { get; set; }
        public void IsActionLogin()
        {
            bIsActionLogin = true;
            bIsActionRegister = false;
        }
        public void IsActionRegister()
        {
            bIsActionLogin = false;
            bIsActionRegister = true;
        }
    }
    
    public class RegisterModel
    {
        [Required]
        [Display(Name = "Email")]
        [RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Email is not valid.")]
        public string UserName { get; set; }
    
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Confirm Password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }
        /*End Model logic*/
    
        /*Begin Controller Logic*/
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginRegisterModel model, string sReturnURL)
        {
            model.IsActionLogin(); //flags that you are using Login Action
            //process your login logic here
        }
    
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(LoginRegisterModel model, string sReturnURL)
        {
            model.IsActionRegister(); //flag Action Register action
            //process your register logic here
        }
        /*End Controller Logic*/
    
    /*Begin View Logic*/
    @model eCommerce.Models.LoginRegisterModel
    @{  
        /*Place this view logic in both the Login.cshtml and Register.cshtml. Now use the last Action called as a Boolean check against your validation messages, so unnecessary validation messages don't show up.*/
        bool bLoginCallBack = Model.bIsActionLogin; 
        bool bRegisterCallBack = Model.bIsActionRegister;
        MvcHtmlString htmlIcoWarn = new MvcHtmlString(" font awesome icon here ");
        MvcHtmlString htmlIcoHand = new MvcHtmlString(" font awesome icon here ");
    }
    
        @using (Html.BeginForm("Login", "Account", new { sReturnURL = Model.sReturnURL }))
        {
            @Html.AntiForgeryToken()
            if (bLoginCallBack)
            {
                MvcHtmlString htmlLoginSummary = Html.ValidationSummary(true);
                if (!htmlLoginSummary.ToHtmlString().Contains("display:none"))
                {
                @:@(htmlIcoWarn)@(htmlLoginSummary)
                }
            }
            @Html.LabelFor(m => m.LoginModel.UserName)
            @Html.TextBoxFor(m => m.LoginModel.UserName, new { @placeholder = "Email" })
        @if (bLoginCallBack)
        {
            MvcHtmlString htmlLoginUsername = Html.ValidationMessageFor(m => m.LoginModel.UserName);
            if (!htmlLoginUsername.ToHtmlString().Contains("field-validation-valid"))
            {
            @:@(htmlIcoHand) @(htmlLoginUsername)
            }
        }
            @Html.LabelFor(m => m.LoginModel.Password)
            @Html.PasswordFor(m => m.LoginModel.Password, new { @placeholder = "Password" })
        @if (bLoginCallBack)
        {
            MvcHtmlString htmlLoginPassword = Html.ValidationMessageFor(m => m.LoginModel.Password);
            if (!htmlLoginPassword.ToHtmlString().Contains("field-validation-valid"))
            {
            @:@(htmlIcoHand) @(htmlLoginPassword)
            }
        }
                @Html.CheckBoxFor(m => m.LoginModel.RememberMe)
                @Html.LabelFor(m => m.LoginModel.RememberMe)
            <button type="submit" class="btn btn-default">Login</button>
        }
    @using (Html.BeginForm("Register", "Account", new { sReturnURL = Model.sReturnURL }))
    {
        @Html.AntiForgeryToken()
    
        if (bRegisterCallBack)
        {
            MvcHtmlString htmlRegisterSummary = Html.ValidationSummary(true);
            if (!htmlRegisterSummary.ToHtmlString().Contains("display:none"))
            {
                @:@(htmlIcoWarn)@(htmlRegisterSummary)
            }
        }
            @Html.LabelFor(m => m.RegisterModel.UserName)
            @Html.TextBoxFor(m => m.RegisterModel.UserName, new { @placeholder = "Email" })
        @if (bRegisterCallBack)
        {
            MvcHtmlString htmlRegisterUsername = Html.ValidationMessageFor(m => m.RegisterModel.UserName);
            if (!htmlRegisterUsername.ToHtmlString().Contains("field-validation-valid"))
            {
            @:@(htmlIcoHand) @(htmlRegisterUsername)
            }
        }
            @Html.LabelFor(m => m.RegisterModel.Password)
            @Html.PasswordFor(m => m.RegisterModel.Password, new { @placeholder = "Password" })
        @if (bRegisterCallBack)
        {
            MvcHtmlString htmlRegisterPassword = Html.ValidationMessageFor(m => m.RegisterModel.Password);
            if (!htmlRegisterPassword.ToHtmlString().Contains("field-validation-valid"))
            {
            @:@(htmlIcoHand) @(htmlRegisterPassword)
            }
        }
            @Html.LabelFor(m => m.RegisterModel.ConfirmPassword)        @Html.PasswordFor(m => m.RegisterModel.ConfirmPassword, new { @placeholder = "Confirm Password" })                
        @if (bRegisterCallBack)
        {
            MvcHtmlString htmlRegisterConfirmPassword = Html.ValidationMessageFor(m => m.RegisterModel.ConfirmPassword);
            if (!htmlRegisterConfirmPassword.ToHtmlString().Contains("field-validation-valid"))
            {
            @:@(htmlIcoHand) @(htmlRegisterConfirmPassword)
            }
            <button type="submit" class="btn btn-default">Signup</button>
        }
    }
    /*End View Logic*/