0

I have an admin page in my MVC5 web application where a user is selected and upon clicking the proceed button a list of all roles with checkboxes appear (via Ajax). If a user is in any of these roles, the checkbox will be checked automatically.

I want to pass a List in my HttpPost method once the user checks/unchecks the boxes for each role. However, the parameter to my action method is null but there are values in Request.Form.

I don't understand why that is. Also do I really need to use @Html.HiddenFor() for each parameter in my viewmodel in order for things to work correctly?

RoleCheckBoxViewModel

public class RoleCheckBoxViewModel
{

    [Display(Name = "Choose Role(s)")]
    [Key]
    public string RoleId { get; set; }
    public string UserId { get; set; }
    public string Name { get; set; }
    [Display(Name="boxes")]
    public bool IsChecked { get; set; }

}

RolesController Action

[HttpPost]
public ActionResult Update(List<RoleCheckBoxViewModel> list) //the model in my view is a List<RoleCheckBoxViewModel> so then why is it null?
{
      //these next two lines are so that I can get to the AddToRole UserManagerExtension 
      var userStore = new UserStore<ApplicationUser>(_context);
        var userManager = new UserManager<ApplicationUser>(userStore);
        foreach (var item in Request.Form) //it's messy but I can see the data in this Form
        {
            System.Console.WriteLine(item.ToString());
        }
        //AddToRole for any new checkboxes checked
        //RemoveFromRole any new checkboxes unchecked
        //context save changes

        return RedirectToAction("Index");
}

The AllRoles Partial View (A result of a previous AJAX call)

@model List<Core.ViewModels.RoleCheckBoxViewModel>



@using (Html.BeginForm("Update", "Roles", FormMethod.Post))
{
<p>
    Select Roles
</p>
<table class="table">

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(model => item.Name)
                @Html.CheckBoxFor(model => item.IsChecked)
            </td>
        </tr>
        @Html.HiddenFor(model => item.RoleId)
        @Html.HiddenFor(model => item.UserId)
        @Html.HiddenFor(model => item.IsChecked)
        @Html.HiddenFor(model => item.Name)

    }
</table>
<input type="submit" value="Save" class="glyphicon glyphicon-floppy-save" />
}
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
Bmoe
  • 888
  • 1
  • 15
  • 37
  • Even though you have a `foreach` and it is generating many checkboxes, they will all have the same name. Use a `for` loop so they have different names. – CodingYoshi Jan 05 '18 at 21:01
  • 1
    model-view-controller tag is for general design pattern, for specific to asp.net-mvc always use asp.net-mvc tag – Ehsan Sajjad Jan 05 '18 at 21:02

1 Answers1

1

The model binder in case of collections needs indexing on the name of the input controls to post in the controller action as collection. you can change your loop to use for loop instead and do indexing in helper methods and yes you will need to create hidden inputs for the properties to be posted if you don't want them to be edited by user.

You can change your loop code to be like following to get model correctly posted back :

@for(int i=0; i < Model.Count' i++)
{
    <tr>
        <td>
            @Html.DisplayFor(model => Model[i].Name)
            @Html.CheckBoxFor(model => Model[i].IsChecked)
        </td>
    </tr>
        @Html.HiddenFor(model => Model[i].RoleId)
        @Html.HiddenFor(model => Model[i].UserId)
        @Html.HiddenFor(model => Model[i].IsChecked)
        @Html.HiddenFor(model => Model[i].Name)

}

Hope it helps!

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160