Search code examples
c#.netasp.net-mvcrazorasp.net-mvc-validation

MVC Nested View Model with Validation


I'm trying to put login and register form into same view. I did everything suggested in other questions but my problem still not fixed.

Here is my parent view authentication.cshtml:

@model Eriene.Mvc.Models.AccountVM
    <div class="row">
        <div class="col-md-6">
            @Html.Partial("_Login", Model.Login ?? new Eriene.Mvc.Models.LoginVM())
        </div>
        <div class="col-md-6">
            @Html.Partial("_Register", Model.Register  ?? new Eriene.Mvc.Models.RegisterVM())
        </div>
    </div>

In my partials I use forms like this:

@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @id = "login-form", @role = "form", @class = "login-form cf-style-1" }))

One of the actions is like this:

[HttpPost]
[AllowAnonymous]
public ActionResult Register(RegisterVM registerVM)
{
    if (ModelState.IsValid)
    {
        User user = new Data.User();
        user.Email = registerVM.Email;
        user.ActivationCode = Guid.NewGuid().ToString();
        user.FirstName = registerVM.FirstName;
        user.LastName = registerVM.LastName;
        user.Password = PasswordHelper.CreateHash(registerVM.Password);
        return RedirectToAction("Index", "Home");
    }

    return View("Authentication", new AccountVM() { Register = registerVM });
}

And here are the models I'm using:

public class AccountVM
{
    public LoginVM Login { get; set; }
    public RegisterVM Register { get; set; }
}

public class RegisterVM
{
    [Required]
    public string Email { get; set; }

    [Required]
    public string FirstName { get; internal set; }

    [Required]
    public string LastName { get; internal set; }

    [Required]
    public string Password { get; internal set; }

    [Compare]
    public string PasswordRetype { get; internal set; }
}

public class LoginVM
{
    [Required]
    public string Email { get; set; }

    [Required]
    public string Password { get; set; }

    public bool RememberMe { get; set; }
}

In the action registerVM's Email property has value, but others are null. ModelState.IsValid is false. What am I doing wrong?


Solution

  • Your properties are not bound because they do not have public setters (only internal) which means the DefaultModelBinder cannot set them (hence they are null and not valid due to the [Required] attributes. Change

    public string FirstName { get; internal set; }
    

    to

    public string FirstName { get; set; }
    

    and ditto for all the other properties with internal setters.