3

I have a PartialView that is a form used to create or modify a user and implements the ViewModel DynamicActionUserModel. The view that referrences this partialView shows a table of all MembershipUsers and gives the ability to create a new user Membership.CreateUser() or modify a user 'Membership.UpdateUser()`. The form in the partialView does an ajax post to my controller to submit data.

The issue I'm running into is that when a user is created their userName, email, password and role is serialized back to the controller as DynamicActionUserModel.RegisterModel and validated, but when a user is modified, password is not a property that is available (nor do I want to make it available to modify on the client side) so it isn't set in DynamicActionUserModel.RegisterModel and ModelState.IsValid is always false.

Perhaps the design of my model and view needs to change, or is there a way to validate the model but ignore password when a user is being modified? Not sure what the best practice is for this one.

I guess another option would be to create another ViewModel and another partialView specifically for modifying a user but that seems sloppy.

Model

public class DynamicActionUserModel {
    public string Action { get; set; }
    public RegisterModel RegisterModel { get; set; }
}

public class RegisterModel {
    [Required]
    [Display(Name = "User Name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    public string[] SelectedRoles { get; set; }
    public MultiSelectList Roles { get; set; }

}

Controller

[HttpGet]
public ActionResult CreateUser() {
    var model = new DynamicActionUserModel {
        Action = "CreateUser",
        RegisterModel = new RegisterModel {
            Roles = new MultiSelectList(System.Web.Security.Roles.GetAllRoles())
        }
    };

    return PartialView("_UserPartial", model);
}

[HttpGet]
public ActionResult ModifyUser() {
    var model = new DynamicActionUserModel {
        Action = "ModifyUser",
        RegisterModel = new RegisterModel {
            Roles = new MultiSelectList(System.Web.Security.Roles.GetAllRoles())
        }
    };

    return PartialView("_UserPartial", model);
}

[HttpPost]
public ActionResult ModifyUser(DynamicActionUserModel model) {
    bool isEqual = true;

    if(!ModelState.IsValid) { // this is always false because password is empty
        return PartialView("_UserPartial", model);
    }

    var user = Membership.GetUser(model.RegisterModel.UserName);
    // do stuff
    Membership.UpdateUser(user);

    return Json(new {success = false});
}

View

@using RobotDog.Models
@model IEnumerable<RobotDog.Models.UserModel>

<!-- table of users -->
<div class="modify-form">
    @Html.Action("ModifyUser")
</div>
<div class="create-user-form">
    @Html.Action("CreateUser")
</div>

PartialView

@model RobotDog.Models.DynamicActionUserModel

@using(Html.BeginForm(Model.Action,"Admin", FormMethod.Post, new { @class = "ajax" })) {
    <!-- Email -->
    @Html.TextBoxFor(x => x.RegisterModel.Email, new { @class = inputSize, placeholder = "Email"})

    <!-- UserName -->
    @if(Model.Action == "ModifyUser") {
        @Html.HiddenFor(x => x.RegisterModel.UserName)
        <span class="input-xlarge uneditable-input">@Html.DisplayNameFor(x => x.RegisterModel.UserName)</span>
    } else {
        @Html.TextBoxFor(x => x.RegisterModel.UserName, new { @class = inputSize, placeholder = "User Name" })
    }

    <!-- Password -->
    @if(Model.Action == "Createuser") {
        @Html.PasswordFor(x => x.RegisterModel.Password, new { @class = inputSize, placeholder = "Password"})
    }

    <!-- Roles -->
    @Html.ListBoxFor(x => x.RegisterModel.SelectedRoles, Model.RegisterModel.Roles)

    <!-- Submit -->
    <input type="submit" value="Submit" class="btn"/>
}
bflemi3
  • 6,698
  • 20
  • 88
  • 155

2 Answers2

2

Try with ModelState.Remove("password") before calling ModelState.IsValid, but as suggested here if a property is not always required you should not mark it as required.

Community
  • 1
  • 1
JOBG
  • 4,544
  • 4
  • 26
  • 47
0

Have you looked at ModelState.IsValidField ?

Note: even as of MVC4 the documentation is backwards and should say :

Determines whether there are any ModelError objects that are associated with or prefixed with the specified key.

I've made a little extension method helper for this:

public static class ModelStateHelpers
    {
        public static bool IsValidFor<TModel, TProperty>(this TModel model,
                                                         System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression,
                                                         ModelStateDictionary modelState)
        {
            string name = ExpressionHelper.GetExpressionText(expression);

            return modelState.IsValidField(name);
        }
    }

Usage is simple:

 bool billingValid = model.IsValidFor(x => x.CheckoutModel.BillingAddress, ModelState);
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689