I have a Manage
page for the current logged in user. This view loads two partial views. One to update the users registered email address and the other to update the password. The Manage
view looks like
@using VisasysNET.Models;
@using Microsoft.AspNet.Identity;
@model VisasysNET.Models.ManageUserViewModel
@{
ViewBag.Title = "Manage Account";
}
<h2>@ViewBag.Title</h2>
<p class="text-success">@ViewBag.StatusMessage</p>
<div class="row">
<div class="col-md-12">
@Html.Partial("_ChangeEmailAddressPartial", Model)
</div>
</div>
<div class="row">
<div class="col-md-12">
@if (ViewBag.HasLocalPassword)
{
@Html.Partial("_ChangePasswordPartial")
}
else
{
@Html.Partial("_SetPasswordPartial")
}
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
I have an "Update Email/Password" button in my update email partial view and update password partial view respectively. Now, I handle the post from the manage view in the controller via
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
bool hasPassword = HasPassword();
ViewBag.HasLocalPassword = hasPassword;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasPassword)
{
if (ModelState.IsValid)
{
IdentityResult result = await UserManager.ChangePasswordAsync(
User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
return RedirectToAction("Manage",
new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
AddErrors(result);
}
}
else
{
// User does not have a password so remove any validation
// errors caused by a missing OldPassword field.
...
}
// If we got this far, something failed, redisplay form.
return View(model);
}
where
public class ManageUserViewModel
{
[Required]
[EmailAddress(ErrorMessage = "Invalid email address.")]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string EmailAddress { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage =
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage =
"The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
The problem is that if the user clicks the button
<input type="submit" value="Update Password" class="btn btn-primary" />
on the update password view, the ManageUserViewModel.EmailAddress
comes back as null and likewise the other way round (if the user clicks the "Update Email Address" button, the password fields come back as null). This is an issue because I then get the validation error messages
In the above I have attempted to update the password. How can I prevent the validation executing for email if the "Update Password" button was pressed and vice-versa?
Thanks for your time.
Request for the Partial Views
Email update partial view is
@using Microsoft.AspNet.Identity
@model VisasysNET.Models.ManageUserViewModel
<p>You're logged in as <strong>@User.Identity.GetUserName()</strong></p>
@using (Html.BeginForm("Manage", "Account", FormMethod.Post,
new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Change Email Address</h4>
<hr />
@Html.ValidationSummary()
<div class="form-group">
@Html.LabelFor(m => m.EmailAddress,
new
{
@class = "col-md-2 control-label"
})
<div class="col-md-10">
@Html.TextBoxFor(m => m.EmailAddress,
new
{
@class = "col-md-10 form-control",
@type = "text",
@placeholder = "Email Address",
@value = Model.EmailAddress
})
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Update Email Address" class="btn btn-primary" />
</div>
</div>
}
The update password partial is
@using Microsoft.AspNet.Identity
@model VisasysNET.Models.ManageUserViewModel
@*<p>You're logged in as <strong>@User.Identity.GetUserName()</strong></p>*@
@using (Html.BeginForm("Manage", "Account", FormMethod.Post,
new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Change Password</h4>
<hr />
@Html.ValidationSummary()
<div class="form-group">
@Html.LabelFor(m => m.OldPassword,
new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.OldPassword,
new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.NewPassword,
new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.NewPassword,
new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.ConfirmPassword,
new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.ConfirmPassword,
new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Change password" class="btn btn-primary" />
</div>
</div>
}
Edit #2. Questions on Answer
The Get method is
// GET: /Account/Manage.
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ?
"Your password has been changed":
message == ManageMessageId.SetPasswordSuccess ?
"Your password has been set" :
message == ManageMessageId.RemoveLoginSuccess ?
"The external login was removed" :
message == ManageMessageId.ChangeEmailAddressSuccess ?
"Your email address was successfully updated" :
message == ManageMessageId.Error ?
"An error has occurred." : "";
ViewBag.HasLocalPassword = HasPassword();
ViewBag.ReturnUrl = Url.Action("Manage");
// Get current email address.
var user = UserManager.FindById(User.Identity.GetUserId());
ManageUserViewModel model = new ManageUserViewModel();
model.EmailAddress = user.EmailAddress;
return View(model);
}
But the view seems to know what OldPassword
is and it is placed in the TextBox, this is null obviously, so is this Firefox being clever?
So are you saying that I should pass in an object[]
containing two view models one for password, the other for the email stuff?