0

I'm new to MVC and I'm facing this problem.

I'm designing a view that allows user to modify their profile and password in 2 different forms.

This is my main action:

public ActionResult ModifyAccount()
{
    User user = this._userManager.FindById(User.Identity.GetUserId());
    ViewBag.User = user;
    return View(new AccountViewModel
    {
        User = user
    });
}

In my ModifyAccount view, i have 2 form:

@using (Html.BeginForm("ModifyPassword", "Account", FormMethod.Post))
{
    // Change password
}

@using (Html.BeginForm("ModifyProfile", "Account", FormMethod.Post))
{
    // Change profile
}

And this is my Model

public class AccountViewModel
    {
        public User User { get; set; } // For change profile

        // For change password
        [Display(Name = "Mật khẩu cũ")]
        public string OldPassword { get; set; }
        [Display(Name = "Mật khẩu mới")]
        public string NewPassword { get; set; }
        [Display(Name = "Xác nhận mật khẩu mới")]
        [Compare("NewPassword", ErrorMessage = "Mật khẩu xác nhận không trùng khớp")]
        public string NewPasswordConfirm { get; set; }
    }

I know that when we use ModelState.IsValid, the whole model will be validated. What I want is if the user submits the ModifyPassword form, only 3 properties OldPassword, NewPassword, NewPasswordConfirm will be validated. Please help, thank you!

Teik Fai
  • 53
  • 7
Khoa Ngo
  • 3
  • 2
  • `ModelState.IsValid` dos not validate anything - its a property which returns a value indicating if the model is valid (which is set by the `DefaultModelBinder`. Do not us data models in your view especially when editing. Use a [view model](http://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) ] - and view models do not contain data models (in your case you will have 2 separate view models or apply conditional validation attributes) –  Apr 02 '17 at 08:22
  • Can you please provide the User Class Model? – Ratheesh Apr 02 '17 at 08:27
  • The user class is auto generated because i use Identity (There are multiple validation in it) – Khoa Ngo Apr 02 '17 at 08:54
  • Normally to exclude some fields from the ModelValidation you have to use " ModelState.Remove("FieldName");". Since you are using no DataAnnotation, this model will pass on ModifyPassword. – Ratheesh Apr 02 '17 at 09:06
  • @StephenMuecke: Thank you! i'll remember that, but can you tell me why we don't use data model for displaying purpose? – Khoa Ngo Apr 02 '17 at 10:14

3 Answers3

0

Use IValidatableObject for custom multiple value validation

public class AccountViewModel : IValidatableObject
{
    [Display(Name = "Mật khẩu cũ")]
    public string OldPassword { get; set; }
    [Display(Name = "Mật khẩu mới")]
    public string NewPassword { get; set; }
    [Display(Name = "Xác nhận mật khẩu mới")]
    [Compare("NewPassword", ErrorMessage = "Mật khẩu xác nhận không trùng khớp")]
    public string NewPasswordConfirm { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (// Do something here)
        {
            yield return new ValidationResult("Error Message");
        }
    }
}

public ActionResult ModifyAccount()
{
    if (!ModelState.IsValid)
    {
    }

}
Sandip Bantawa
  • 2,822
  • 4
  • 31
  • 47
0

Create a ViewModel with the 3 properties OldPassword, NewPassword, NewPasswordConfirm and receive that as the parameter in the Post version of ModifyPassword:

Class ModifyPasswordView 
{
    public string OldPassword {get;set;}
    public string NewPassword {get;set;}
    public bool NewPasswordConfirm {get;set;}
}

[HttpPost]
public ActionResult ModifyPassword( ModifyPasswordView passwordView)
{
     if ( ModelState.IsValid )
     {
        ...
     }
}

The modelstate check above only looks at the 3 fields in the ModifyPasswordView model, regardless of the fields present in the form

Gene Stempel
  • 285
  • 1
  • 5
  • so i changed my model to contain User and ModifyPasswordView like you said. but i received a model with null properties in ModifyPassword. – Khoa Ngo Apr 02 '17 at 08:53
0

Create a dedicated view model for update password:

public class ModifyPasswordViewModel
{
    [Display(Name = "Mật khẩu cũ")]
    [Required]
    public string OldPassword { get; set; }

    [Display(Name = "Mật khẩu mới")]
    [Required]
    public string NewPassword { get; set; }

    [Display(Name = "Xác nhận mật khẩu mới")]
    [Required]
    [Compare("NewPassword", ErrorMessage = "Mật khẩu xác nhận không trùng khớp")]
    public string NewPasswordConfirm { get; set; }
}

Create a partial view _ModifyPasswordViewModel.cshtml. Since you display multiple views on the same page, I would use Ajax.BeginForm instead of Html.BeginForm just for modifying password via ajax and not via full form post.

_ModifyPasswordViewModel.cshtml:

@model ModifyPasswordViewModel

@using (Ajax.BeginForm("ModifyPassword", "Account", new AjaxOptions { OnSuccess = "changePasswordSuccess"}))
{
    <div class="form-group">
       @Html.LabelFor(m=>m.OldPassword)
       @Html.PasswordFor(m=>m.OldPassword)
       @Html.ValidationMessageFor(m=>m.OldPassword)
    </div>
    ...
}

In your main view render partial:

<div id="changePassword-container">
  @{Html.RenderPartial("_ModifyPasswordViewModel", new ModifyPasswordViewModel());}
<div/>

Controller action:

[HttpPost]
public ActionResult ModifyPassword(ModifyPasswordViewModel model)
{
     if ( this.ModelState.IsValid )
     {
        ...
     }
}

P.S. in order for client side validation to work you need to include jquery.validate.js and jquery.validate.unobtrusive.js in the view (layout page) and to enable it in web config:

<configuration>
  <appSettings>  
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
</configuration>
Alex Art.
  • 8,711
  • 3
  • 29
  • 47
  • This is exactly what i want, but how can i pass the model back to the main action to show validation error? – Khoa Ngo Apr 02 '17 at 10:31
  • 1. Validation errors are shown instantly because of client side validation. 2. I defined OnSuccess handler in Ajax.BeginForm (changePasswordSuccess), it will be triggered if server returns 2XX status in response. You can define OnError handler and in case of validation failure return BadRequest status code( 400). This handler could be used to display a message to users – Alex Art. Apr 02 '17 at 10:42
  • the problem is when you pass `new ModifyPasswordViewModel()` to Html.RenderPartial, the validation message is displayed instantly because all fields are required, how to avoid that? – Khoa Ngo Apr 02 '17 at 11:21
  • Problem solved by passing a flag to ViewData to indicate when to display error message. Thank you so much Alex, you saved my day! :) – Khoa Ngo Apr 02 '17 at 11:30