Reputation: 2318
I have a MasterModel class that basically handles a lot of data for partial views across shared layouts. However, when I try and return part of the object (or the entire MasterModel) in a form, such as "Login", no data is passed to the controller.
Here is part of the MasterModel:
public class MasterModel
{
public Login Login;
public Account Account;
public User User;
public MasterModel()
{
Login = new Login();
Account = new Account();
User = new User();
}
}
Here is part of the Login:
public class Login
{
public string Email {get;set;}
public string PasswordHash {get; private set;
public string FName {get;set;}
}
Here is part of the master Layout template that calls the partial view for a login:
@model Models.MasterModel
...
@{
Html.RenderPartial("../Partials/_Login", Model.Login);
}
...
And here is the _Login partial:
@model Models.Login
<div class="login-bar">
@if (Model != null && !string.IsNullOrWhiteSpace(Model.FName) )
{
var welcomeString = "Welcome back, " + Model.FName;
<p>@Html.ActionLink(welcomeString, "Overview", "Account")</p>
}
else
{
using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<p>
@Html.Label("Email Address:")
@Html.EditorFor(login => login.Email, new { @class = "email" })
@Html.Label("Password:")
@Html.PasswordFor(login => login.PasswordHash)
<input type="submit" value="Login" class="button" />
@if (ViewBag.Message != null)
{
<shim class="red">@ViewBag.Message.ToString()</shim>
}
</p>
}
}
</div>
Here's the AccountController for the HttpPost for Login too:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(Login login, string returnUrl)
{
var master = new MasterModel();
// ModelState is valid. login.Email = "" and login.PasswordHash is still default
if (ModelState.IsValid && (!string.IsNullOrEmpty(login.Email) && !string.IsNullOrEmpty(login.PasswordHash)))
{
var user = await UserManager.FindAsync(login.Email, login.PasswordHash);
if (user != null)
{
await SignInAsync(user, true);
master.User = user;
return RedirectToAction("Overview", "Account", master);
}
}
return View(master);
}
I've also tried passing the entire MasterModel to the partial view instead (also changing the @model to use Models.MasterModel) whilst accessing the Login property as masterModel.Login.Email, etc, to no avail.
It's got to be something pretty simple that I'm missing with the binding on the property names, but I'm starting to go code blind now!
Any ideas?
[UPDATE] - Added AccountController
Upvotes: 0
Views: 747
Reputation: 2318
After 7 hours of hunting, I can now kick myself.
The login model I posted above was missing one thing that I chose to omit out of speed. Here's what it should have looked like in my original question above:
public class Login
{
public string Email {get; private set;}
public string PasswordHash {get; private set;}
public string FName {get; private set;}
}
Changing these to public properties (the PasswordHash slightly differently though) has solved the mystery of the missing object:
public class Login
{
public string Email {get;set;}
public string PasswordHash {get; set;} // Not actual implementation. Applies it's own crypto first
public string FName {get;set;}
}
Hopefully this helps someone else (and myself again in a few weeks!) from banging their head against the wall!
Upvotes: 1
Reputation: 13949
Everything seems ok.. You might try renaming your parameter in your Login action to something besides login
public async Task<ActionResult> Login(Login model, string returnUrl)
and maybe do the same in your partial
@Html.EditorFor(model => model.Email, new { @class = "email" })
Upvotes: 0