1

I have a _layout page which has a login box (partial view) and that view has it's own model. So the controller looks like this:

        public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(LoginModel loginModel)
    {
        if(ModelState.IsValid)
        {
            var g = new GallaryImage();
            var user = g.LoginUser(loginModel.Username, loginModel.Password);
            if(user != null) 
            {
                FormsAuthentication.SetAuthCookie(user.username, false);
                return RedirectToAction("Index", "Home");
            }
            ModelState.AddModelError("", "Invalid Username/Password");
        }
        return View(loginModel);
    }

But as soon as my main content page needs a model, my web app fails because the Login box expects a LoginModel type, but my content page is sending a different model:

This is the GET method for my main Index screen:

 public ActionResult Index()
    {
        IndexModel model = new IndexModel();
        var g = new GallaryService.GallaryImage();
        var i = g.GetRandomImage();

        if (i != null)
            model.RandomImageUrl = "~/Images/Watermarks/" + i.filename;
            return View(model);
    }

So, my main content page has an IndexModel, but my partial view has a LoginModel. When I try run it, I get an error:

"The model item passed into the dictionary is of type 'GalleryPresentation.Models.IndexModel', but this dictionary requires a model item of type 'GalleryPresentation.Models.LoginModel'."

How do I handle this - My _layout needs the model for the login box.

As requested, here is the Loginbox cshtml file.

    @using GalleryPresentation.Models
@model LoginModel

<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
@using (Html.BeginForm("index", "Account", FormMethod.Post))
{
    <table class="smallBox">
        <tr>
            <td>@Html.LabelFor(m => m.Username)</td>
            <td>@Html.TextBoxFor(m => m.Username, new { @class = "smallText" })</td>
            <td>@Html.LabelFor(m => m.Password)</td>
            <td>@Html.PasswordFor(m => m.Password, new { @class = "smallText" })</td>
        </tr>
        <tr>
            <td colspan="4" align="right"><input type="submit" value="Login"/></td>
        </tr>
        <tr>
            <td colspan="2">@Html.ValidationSummary()</td>
        </tr>
    </table>

}

And the Index.cshtml file (THe main content screen) has this:

    @using GalleryPresentation.Models
@model IndexModel
@{
    ViewBag.Title = "Craig and Melanie's Digital Moments";
}

<br/>
<div style="text-align: center">
    <img src="@Url.Content( Model.RandomImageUrl)" alt="@ViewBag.Title" />
</div>
3
  • Please include the Login Box partial view cshtml. Commented Jan 15, 2012 at 2:57
  • Looks like I can use my LoginModel as a base class for all other models, and then inherit it? That seems to work - but is that good design/practise? I need to ensure that all models inherit from LoginModel (which I guess I would rename to BaseModel - incase something else is needed?). Commented Jan 15, 2012 at 3:24
  • 1
    That is an option, but I would not advise that. Treating your LoginModel as a base model implies that every one of your view models is an extension of a LoginModel. Commented Jan 15, 2012 at 3:46

1 Answer 1

1

Questions like this aren't always the easiest to answer because there isn't a straightforward solution. There are several issue though that should be considered. If it is possible, I would recommend that you handle login validation errors in a separate view. The partial view for the small login box then does not require a strongly-typed view model.

There's no perfect solution, but I don't think that it makes a lot of sense for you to always be creating LoginModel objects on every request that renders a view which depends on _Layout. The solution below advocates the creation of a separate login view which can be used for explicit login attempts and for the handling of any login failures.

If you have any trouble following this, feel free to your question in a comment and I'll do my best to answer.

Login Box

@using (Html.BeginForm("Index", "Account"))
{
    <table class="smallBox">
        <tr>
            <td>Username</td>
            <td>@Html.TextBox("Username", new { @class = "smallText" })</td>
            <td>Password</td>
            <td>@Html.Password("Password", new { @class = "smallText" })</td>
        </tr>
        <tr>
            <td colspan="4" align="right"><input type="submit" value="Login"/></td>
        </tr>
    </table>
}

Account Controller

public ActionResult Login()
{
    return View();
}

public ActionResult RetryLogin()
{
    ModelState.AddModelError(null, "The Username or Password you entered is invalid.  Please try again.");
    return View("Login");
}

[HttpPost]
public ActionResult Index(LoginModel loginModel)
{
    if(ModelState.IsValid)
    {
        var g = new GallaryImage();
        var user = g.LoginUser(loginModel.Username, loginModel.Password);
        if(user != null) 
        {
            FormsAuthentication.SetAuthCookie(user.username, false);
            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError("", "Invalid Username/Password");
    }

    return RedirectToAction("RetryLogin");
}

Login View

@using (Html.BeginForm("Index", "Account"))
{
    @Html.ValidationSummary()
    <!-- login form here -->
}
Sign up to request clarification or add additional context in comments.

2 Comments

Looks like an amazing answer. I'm not understanding how the Loginbox fits onto the _layout - or are we saying that the Loginbox is no longer on the _layout, and that it's a separate Login screen?
I'm suggesting that you have both a partial view used in the _layout and a full view which handles the /Account/Login scenario. The partial view at that point it probably not real necessary, but it may still be helpful to split that out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.