2

I've been trying to learn asp.net MVC coming from a webforms background and I believe I didn't get the idea of actions, controllers and their relation to partials very well...

I have a _Layout.cshtml file that includes a _LoginPartial.cshtml (it's not the default code from Visual Studio, just the same name) and a Home/Index.cshtml file which includes the same partial (_LoginPartial) as well, here's the code for the partial:

@model LoginModel
@if (Request.IsAuthenticated) {
    @*<welcome text here>*@
} else {
    if ( Model.Layout == LoginLayout.Compact ) {
        @*<simple layout here>*@
    } else {
        <form method="post" action="~/Profile/Login">
            @Html.AntiForgeryToken()

            @Html.LabelFor(m => m.Email, "Email Field")
            @Html.TextBoxFor(m => m.Email)
            @Html.ValidationMessageFor(m => m.Email)

            @Html.LabelFor(m => m.Password, "Password Field")
            @Html.PasswordFor(m => m.Password)
            @Html.ValidationMessageFor(m => m.Password)

            @Html.ValidationSummary()
        </form>
    }
}

so the _Layout.cshtml file includes the _LoginPartial.cshtml with the following code (no form tag inside _Layout.cshtml):

@Html.Partial("_LoginPartial", new LoginModel())

and the Home/Index.cshtml includes the same partial with the following code (no form tag besides the one inside _LoginPartial either):

@Html.Partial("_LoginPartial", new LoginModel() {
    Layout = LoginLayout.Full
})

so instead of using the default "AccountController" I decided to create a new one (PostController) to try, the partial above posts it's data successfully to ProfileController, but when the data is incorrect the ValidationMessage is always empty, here's the code to my controller:

public class ProfileController : BaseController
{
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel form)
    {
        if (ModelState.IsValid)
        {
            //code to validate the user here
            if (userIsValid)
            {
                FormsAuthentication.SetAuthCookie(form.Email, false);
                return RedirectToAction("Index");
            }
        }

        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return RedirectToAction("Index", "Home");
    }
} 

as you can see, the partial is accessed via Home/Index.cshtml but its controller is located at Profile/Login, so when it redirects back to the Home/Index page no validation message is shown...

my question is: am I doing the right thing? how can I show the ValidationMessage from my model? if I'm not doing the right thing, how these actions and controllers are supposed to be structured to be true to the MVC model?

and just for sake of completeness, here's my LoginModel class:

public enum LoginLayout {
    Compact,
    Full
}
public class LoginModel
{
    public LoginLayout Layout { get; set; }

    [Required(ErrorMessage = "Please enter your e-mail")]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    [Required(ErrorMessage = "Please enter your password")]
    [DataType(DataType.Password)]
    public string Password { get; set; }
}
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
leandro koiti
  • 423
  • 1
  • 7
  • 18
  • 1
    possible duplicate of [ASP.NET MVC - How to maintain ModelState from a different controller?](http://stackoverflow.com/questions/4522316/asp-net-mvc-how-to-maintain-modelstate-from-a-different-controller) in Summary - ModelState is not maintained during any `Redirect`s (I'm aware of) in MVC. – Erik Philips Aug 02 '13 at 16:55
  • indeed I found the answer in the mentioned post (the same as the author voted as answer), thanks a lot and sorry for duplicating it – leandro koiti Aug 02 '13 at 20:43

1 Answers1

2

You are doing quite the right thing, but out the blue just like that, do you have the validation message for the Required annotations ? Or is it just missing for the one you added to the model state ? If it missing for all of them

@Html.Partial("_LoginPartial", new LoginModel())

Might be the problem, have you tried @Html.EditorFor instead (edit: forgot to mention that you should then move your partial view _LoginPartial to the root editor template folder)? You should also use that for the password and email btw.

In any case, the error added to the model state don't have a place for MVC to display, you will need something along the line of @Html.ValidationSummary(true) I think remember reading something about displaying only specific validation errors, let me see if I can dig that for you

EDIT: Just spotted that, use @using(@Html.BeginForm( as well instead of manually putting the form like that, it easier and more Razor friendly (you will be able to ajaxify that later on quite easily, which I would recommend for a logging form like that)

EDIT 2: haHA Here is what I'm talking about, link, the answer here will allow you to display a specific model state error if it exist, but you'll need to give it a key when you add it to the model state

Community
  • 1
  • 1
Simon Rapilly
  • 393
  • 1
  • 13
  • 2
    hey Simon thanks a lot, I tried everything you said but the only thing that really worked for me I found in the link that Erik Philips posted as duplicate, thanks again! – leandro koiti Aug 02 '13 at 20:43
  • Meh no problem, just a quick question though, is that like a loging form you keep on top the page ? Something along those line ? – Simon Rapilly Aug 02 '13 at 21:28
  • exactly, on my layout I use the same partial to render that "Register | Login" on the top of the page and then on the login page it renders a full form... is it a bad practice to do this way? thanks again! – leandro koiti Aug 03 '13 at 16:10
  • Most certainly not, especially if a couple of your pages have a public/anonymous access, just wanted to be sure I understood your initial problem right. I would suggest however to keep a redirection url somewhere, and redirect the user there once they are successfully logged in, so they don't have to re-navigate to the page they want to reach. – Simon Rapilly Aug 06 '13 at 08:28