Search code examples
c#asp.netasp.net-mvcasp.net-mvc-4asp.net-mvc-5

mvc5 model which is posted with html.beginform is missing some properties


I have this model which I'm using in a view:

using (var db = new FieldSaverContext())
{
    AspNetUser user = (from p in db.AspNetUsers
                       where p.Id == id
                       select p).Include(m => m.AspNetRoles) .First();
    model.AUser = user;

    List<AspNetRole> roles = (from r in db.AspNetRoles
                              select r).ToList();
    model.RoleList = roles;

    List<ListID> ids = (from l in db.ListIDs
                        select l).ToList();
    model.ListIDList = ids;

    List<ViewerList> viewerLists = (from v in db.ViewerList
                                    select v).ToList();
    model.ViewerList = viewerLists;

}

This is how my view looks like:

@using (Html.BeginForm("Edit", "Users"))
{
  <div class="container containerEdit">
    <div class="col-md-9">
      <div class="col-md-6">
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.Id)
          <label>@Html.DisplayFor(m => m.AUser.Id)</label>
        </div>
        <div class="form-group">
          @Html.DisplayFor(m => m.AUser.FirstName)
          @Html.TextBoxFor(m => m.AUser.FirstName)
        </div>
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.PhoneNumber)
          @Html.TextBoxFor(m => m.AUser.PhoneNumber)
        </div>
        <div class="form-group">
          @Html.LabelFor(m => m.ListIDList)
          @Html.DropDownListFor(n => n.ListIDList, new SelectList(Model.ListIDList, "ID", "Value"))
        </div>
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.Note1)
          @Html.TextBoxFor(m => m.AUser.Note1)
        </div>
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.Note2)
          @Html.TextBoxFor(m => m.AUser.Note2)
        </div>
      </div>
      <div class="col-md-6">
        <div class="form-group">&nbsp;</div>
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.LastName)
          @Html.TextBoxFor(m => m.AUser.LastName)
        </div>
        <div class="form-group">
          @Html.LabelFor(m => m.AUser.Email)
          @Html.TextBoxFor(m => m.AUser.Email)
        </div>
        <div class="form-group">
          <label>Report Viewer</label>
          @Html.DropDownListFor(v => v.AUser.ViewerId, new SelectList(Model.ViewerList, "ID", "Value"))
        </div>
      </div>
      @Html.HiddenFor(v => v.AUser.Id)
    </div>
    <div class="col-md-3">
      @foreach (AspNetRole role in Model.RoleList)
      { @Html.CheckBox(role.Name, Model.AUser.AspNetRoles.Any(userRole => userRole.Id == role.Id)) @Html.Label(role.Name)<br /> }
    </div>
  </div>
  <div class="col-md-8">
    <input type="submit" class="btn btn-success btn-lg btnSaveEdit" value="Save" />
  </div>
}  

When the postback is happening, I'm able to get the values for the AUser but I'm not getting any of the values for the Model.RoleList which is used in checkbox. I want to check which checkboxes are checked from the RoleList and assign those roles to the User.

What am I missing?

Update

Edit Action(s):

[HttpGet]
public ActionResult Edit(int? id)
{
    ManageViewModel model = new ManageViewModel();

 
        using (var db = new DbContext())
        {
            AspNetUser user = (from p in db.AspNetUsers
                               where p.Id == id
                               select p).Include(m => m.AspNetRoles) .First();
            model.AUser = user;

            List<AspNetRole> roles = (from r in db.AspNetRoles
                                      select r).ToList();

            model.RoleList = roles;

            List<ListID> ids = (from l in db.ListIDs
                select l).ToList();

            model.ListIDList = ids;

            List<ViewerList> viewerLists = (from v in db.ViewerList
                select v).ToList();

            model.ViewerList = viewerLists;

        }
    return View(model);
}

[HttpPost]
public ActionResult Edit(ManageViewModel model)
{
    AspNetUser user = model.AUser;

    using (var db = new DbContext())
    {
        AspNetUser dbUser = (from u in db.AspNetUsers
            where u.Id == model.AUser.Id
            select u).First();

        dbUser.FirstName = model.AUser.FirstName;
        dbUser.LastName = model.AUser.LastName;
        dbUser.Email = model.AUser.Email;
        dbUser.PhoneNumber = model.AUser.PhoneNumber;

        dbUser.Note1 = model.AUser.Note1;
        dbUser.Note2 = model.AUser.Note2;
        dbUser.ViewerId = model.AUser.ViewerId;

        db.SaveChanges();
    }

    return View();
}

Solution

  • The issue is that the roles are not bound to your view model. So here's what you have to do:

    • Create a new role view model:

      public class RoleViewModel
      {
        public int Id { get; set; }   
        public string Name { get; set; }
        public bool Selected { get; set; }
      }
      
    • Modify your ManageUserViewModel to have a list of RoleViewModel instead of AspNetRole:

      public class ManageUserViewModel
      {
        public List<RoleViewModel> Roles { get; set;}
        // Your other properties
      }
      
    • Change your edit to set the Roles property from the available roles:

      public ActionResult Edit(int? id)
      {
         // beginning of your action
         List<AspNetRole> roles = from r in db.AspNetRoles
                                  select r;
         model.Roles = new List<RoleViewModel>();
         foreach(AspNetRole role in roles){
           model.Roles.Add(new RoleViewModel { 
             Id = role.Id, 
             Name = role.Name,
             Selected = user.AspNetRoles.Any(userRole => userRole.Id == role.Id)
           });
         }
         // end of your action
      }
      
    • Change your other action to

       public ActionResult Edit(ManageViewModel model){
        // beginning of your action
        // remove roles from user
        List<AspNetRoles> newRoles = new List<AspNetRoles>();
        foreach(RoleViewModel role in model.Roles){
          newRole.Add(db.AspNetRoles.Find(role.id))
        } 
        // set your user roles from newRoles
        // end of your action 
      }
      
    • In your view change the checkbox part by :

      @Html.EditorFor(model => model.Roles)
      
    • And finally create a Shared/EditorTemplate/RoleViewModel.cshtml file :

      @model RoleViewModel
      @Html.HiddenFor(model => model.Id)
      @Html.CheckBoxFor(model => model.Selected)
      @Html.DisplayFor(model => model.Name)