I have an MVC 3 project where I have 1 view LoginRegister which contains 2 views with forms for login and pre-register. The problem is after incorrectly completing the pre-register form and using PartialView("LoginRegister", loginRegisterViewModel) validation messages aren't displayed due the ModelState being lost. Before reading the next paragraph it's probably best to jump to the CODE.
Debugging the PartialView("LoginRegister", loginRegisterViewModel) and stepping into the PreRegister view to the following @Html.ErrorMessageFor(model => model.Email). Where the ModelState does not contain the Email key (see below) and hence doesn't display an error message.
private static MvcHtmlString ErrorMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes)
{
string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName)) // ModelState contains no keys, e.g. Email not found and a null is returned
{
return null;
}
// code continues here to render error message
}
CODE
ViewModels
LoginRegisterViewModel
namespace Site.ViewModels.Account
{
public class LoginRegisterViewModel
{
public bool IsDialog { get; set; }
public PreRegisterViewModel PreRegister { get; set; }
public QuickLoginViewModel QuickLogin { get; set; }
}
}
PreRegisterViewModel
using FluentValidation.Attributes;
using Site.ViewModels.Validators;
namespace Site.ViewModels.Account
{
[Validator(typeof(PreRegisterViewModelValidator))]
public class PreRegisterViewModel
{
public string Email { get; set; }
public string ConfirmEmail { get; set; }
}
}
Views
LoginRegister
@model Site.ViewModels.Account.LoginRegisterViewModel
@{
Layout = ViewBag.Layout;
}
<div id="loginReg" class="mod-loginReg">
<h2>Login and Registration</h2>
@{Html.RenderPartial("PreRegister", Model.PreRegister, new ViewDataDictionary());}
@{Html.RenderPartial("QuickLogin", Model.QuickLogin, new ViewDataDictionary());}
</div>
PreRegister
@using Site.Classes.MVC.Extensions;
@model Site.ViewModels.Account.PreRegisterViewModel
@using (Html.BeginForm("PreRegister", "Account", FormMethod.Post, new { @class = "form-errorContainer" }))
{
<div class="mod-loginReg-register">
<h3>Register</h3>
<p>Please enter your email address to register.<br /><br /></p>
<div class="mod-loginReg-form">
<div class="field-item">
@Html.LabelFor(model => model.Email)
@Html.TextBoxFor(model => model.Email, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.Email)
</div>
<div class="field-item">
@Html.LabelFor(model => model.ConfirmEmail)
@Html.TextBoxFor(model => model.ConfirmEmail, new { @maxlength = "255", @class = "Textbox required email" })
@Html.ErrorMessageFor(model => model.ConfirmEmail)
</div>
<div class="button-group">
@Html.Button("Register", "Register")
</div>
</div>
</div>
}
AccountController
[RequireHttps]
public ActionResult LoginRegister(int showDialog)
{
var loginRegisterViewModel = new LoginRegisterViewModel();
if (showDialog == 1)
{
ViewBag.Layout = "~/layouts/structure/dialog.cshtml";
loginRegisterViewModel.IsDialog = true;
}
else
{
ViewBag.Layout = "~/layouts/structure/2column.cshtml";
}
return PartialView(loginRegisterViewModel);
}
[RequireHttps]
public ActionResult PreRegister()
{
return PartialView();
}
[HttpPost]
[RequireHttps]
public ActionResult PreRegister(PreRegisterViewModel model)
{
if (ModelState.IsValid)
{
return PartialView("PreRegisterComplete");
}
var loginRegisterViewModel = new LoginRegisterViewModel { PreRegister = model, QuickLogin = new QuickLoginViewModel() };
return PartialView("LoginRegister", loginRegisterViewModel);
}