3

I am developing an Asp.Net mvc application. In my project there are two forms in a single view like this:

enter image description here

To use multiple forms in a single view, there is a problem for validation using data annotation. First I found this link for using multiple forms in single view asp.net MVC 4 multiple post via different forms. Following answer of that link, I can add multiple forms. But I cannot validate using data annotation with that approach. So I googled for another solution.

This link (http://geekswithblogs.net/MightyZot/archive/2013/11/11/html.validationsummary-and-multiple-forms.aspx) also just the same, I have to add validation logic in controller. I found another solution (http://bluetubeinc.com/blog/2012/10/validating-multiple-forms-the-asp-dot-net-mvc3-way-using-strongly-typed-views). I thought this solution will solve my problem to validation using data annotation. But one thing to notice is that it is working with Ajax Form. But I followed that link and I am still having problem.

These are my view models:

public class AuthVM
    {
        public LoginViewModel LoginForm { get; set; }
        public RegisterViewModel RegisterForm { get; set; }
    }
public class LoginViewModel
    {
        [Required]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    }

    public class RegisterViewModel
    {
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [MaxLength(70)]
        public string Email { 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; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

This is my controller and actions for login and register:

public class AccountController: Controller
{

[AllowAnonymous]
        public ActionResult Create(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            AuthVM model = new AuthVM
            {
                RegisterForm = new RegisterViewModel(),
                LoginForm = new LoginViewModel()
            };
            return View("Auth",model);
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
           //check modelstate and continue
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            //check modelstate and continue
        }

}

This is my view:

<section id="form">
    <!--form-->
    <div class="container">
        <div class="row">
            <div class="col-sm-4 col-sm-offset-1">
                <div class="login-form">
                    <!--login form-->
                    <h2>Login to your account</h2>
                    @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, null))
                    {
                        @Html.AntiForgeryToken()
                        @Html.ValidationSummary()
                        @Html.TextBoxFor(x => x.LoginForm.Email, new { placeholder = "Email" })
                        @Html.PasswordFor(x => x.LoginForm.Password, new { placeholder = "Password" })
                        <button type="submit" class="btn btn-default">Login</button>
                    }
                </div><!--/login form-->
            </div>
            <div class="col-sm-1">
                <h2 class="or">OR</h2>
            </div>
            <div class="col-sm-4">
                <div class="signup-form">
                    <!--sign up form-->
                    <h2>New User Signup!</h2>
                    @using (Html.BeginForm("Register", "Account", FormMethod.Post))
                    {
                        @Html.AntiForgeryToken()
                        @Html.ValidationSummary()
                        @Html.TextBoxFor(x => x.RegisterForm.UserName, new { placeholder = "Display Name" })
                        @Html.TextBoxFor(x => x.RegisterForm.Email, new { placeholder = "Email" })
                        @Html.PasswordFor(x => x.RegisterForm.Password, new { placeholder = "Password" })
                        @Html.PasswordFor(x => x.RegisterForm.ConfirmPassword, new { placeholder = "Retype password" })
                        <button class="btn btn-primary">Register</button>
                    }
                </div><!--/sign up form-->
            </div>
        </div>
    </div>
</section><!--/form-->
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

But when I submit the form, model state is always invalid because values are always null even if I input correct values. For example, I login, then form will submit to post login action, but value are always null.

enter image description here

Why is that always null? How can I bind to get correct values? How can I validate multiple forms in a single view (strongly typed view) using data annotation?

halfer
  • 19,824
  • 17
  • 99
  • 186
Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372
  • Its `null` because the model in your view is `AuthVM`, but the parameter in your POST method is `LoginViewModel` (not `AuthVM`) and `LoginViewModel` does not contain a property named `RegisterForm` –  Jul 09 '16 at 10:53
  • I tried add RegisterForm property to AuthVM. Not working as well. If I bind using AuthVM data annotation will validate both forms. So please how can I solve it? – Wai Yan Hein Jul 09 '16 at 11:03
  • You can use `public asyn Login [Bind(Prefix="RegisterForm")](LoginViewModel model)` But I would question why you are creating a page like that (instead of having a `Login` view that includes a link to a Register view) –  Jul 09 '16 at 11:07
  • 1
    I also do not want to do that. But I have to follow silly designer as possible. Thanks so much for your support. I got that. – Wai Yan Hein Jul 09 '16 at 11:16

1 Answers1

2

As @stephen-muecke says doing this will work:

public async Task<ActionResult> Register([Bind(Prefix="RegisterForm")]RegisterViewModel model)
        {
            //check modelstate and continue
        }

If you don’t want to do that then you can create a partial view for each form and pass the model required for that form to that partial view.

<section id="form">
    <!--form-->
    <div class="container">
        <div class="row">
            <div class="col-sm-4 col-sm-offset-1">
                <div class="login->
                     @Html.Partial("_LoginForm", Model.LoginForm)
                </div><!--/login form-->
            </div>
            <div class="col-sm-1">
                <h2 class="or">OR</h2>
            </div>
            <div class="col-sm-4">
                <div class="signup-form">
                     @Html.Partial("_RegisterForm", Model.RegisterForm)
                </div><!--/sign up form-->
            </div>
        </div>
    </div>
</section><!--/form-->
Jon Ryan
  • 1,497
  • 1
  • 13
  • 29