POST到MVC控制器当我没有按顺序选中复选框时,IEnumerable嵌套模型为null

时间:2015-06-09 17:01:48

标签: c# asp.net-mvc razor visual-studio-2013 asp.net-mvc-5

当我试图在Post中获取值时,当我不按顺序(1,2,3等)检查时,复选框的值被设置为NULL。

我需要选择任何顺序(即4,5)。

MODEL:

public class AssignUsersViewModel
{
    [Display(Name = "Check to select")]
    public bool Select { get; set; }

    public int Id { get; set; }

    [Display(Name = "App. Username")]
    public string UserName { get; set; }

    [Required]
    public string GivenName { get; set; }

    [Required]
    public string Surname { get; set; }

    [Display(Name = "Roles")]
    public IList<Roles> Roles { get; set; }
}

public class AssignUsersAddModel
{
    public bool Select { get; set; }

    public int Id { get; set; }

    public IEnumerable<SelectedRoles> selectedRoles { get; set; }
}

public class SelectedRoles
{
    public string Name { get; set; }
}

CSHTML:

@model IList<AspNetIdentity2DRH.Models.AssignUsersViewModel>
@using (Html.BeginForm("UsersAddToApp", "UsersAdmin", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <table class="table">
        <tr>
            <th>Check for add</th>
            <th>Username</th>
            <th>Givenname</th>
            <th>Surename</th>
            <th>Roles</th>
        </tr>
        @for (int i = 0; i < Model.Count(); i++)
        {
            <tr>
                <td>
                    @Html.CheckBoxFor(x => x[i].Select)
                    @Html.HiddenFor(x => x[i].Id)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].UserName)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].GivenName)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].Surname)
                </td>
                <td>
                    <div class="row">
                        <div class="form-group">
                            @for (int j = 0; j < Model[i].Roles.Count(); j++)
                            { 
                                <div class="col-sm-4">
                                    <input type="checkbox" name="[@i.ToString()].selectedRoles[@j.ToString()].Name" value="@Model[i].Roles[j].Name" class="checkbox-inline" />
                                    @Html.Label(Model[i].Roles[j].Name, new { @class = "control-label", @data_toggle = "tooltip", @data_placement = "top", @data_original_title = Model[i].Roles[j].Description })
                                </div>
                            }
                        </div>
                    </div>
                </td>
            </tr>
        }
    </table>
    <p>
        <input type="submit" value="Add existing user" class="btn btn-primary" />
        <input type="button" value="Cancel" onclick="window.location.href = '@Url.Action("UsersIndex", "UsersAdmin")';" class="btn btn-cancel" />
    </p>
}

控制器:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UsersAddToApp(List<AssignUsersAddModel> model)
{
    if (ModelState.IsValid)
    {
        foreach (AssignUsersAddModel item in model)
        {
            if (item.Select)
            {
                using (DbContextTransaction dbtrans = db.Database.BeginTransaction())
                {
                    try
                    {
                        int appId = (int)Session["ApplicationId"];
                        Users user = UserManager.FindById(item.Id);
                        db.ApplicationUsers.Add(new ApplicationUsers { ApplicationId = appId, UserId = user.Id });
                        db.SaveChanges();
                        foreach (SelectedRoles RolesItem in item.selectedRoles)
                        {
                            int roleId = db.Roles.Where(r => r.Name == RolesItem.Name).Select(r => r.Id).FirstOrDefault();
                            db.UserApplicationRoles.Add(new UserApplicationRoles { ApplicationId = appId, UserId = user.Id, RoleId = roleId });
                            db.SaveChanges();
                        }
                        dbtrans.Commit();
                    }
                    catch (Exception)
                    {
                        dbtrans.Rollback();
                    }
                }

            }
        }
        return RedirectToAction("UsersAddToApp");
    }
    ModelState.AddModelError("", "An error has occurred. Contact system administrator.");
    return RedirectToAction("UsersAddToApp");
}

问题是当我选择复选框时(除了第一个,或最后一个在中间,行:

foreach (SelectedRoles RolesItem in item.selectedRoles)

发送item.selectedRoles为空。

我怎么能做到这一点?

3 个答案:

答案 0 :(得分:5)

DefaultModelBinder只会绑定集合项的索引器从零开始并且是连续的集合。您的问题是您手动创建复选框元素。由于未选中的复选框不会回发,如果您取消选中,则在您提交时,它和任何后续复选框值都不会绑定到该集合。

接下来尝试将复选框绑定到string值。复选框有2个状态,用于表示布尔值。

您尚未向您显示Role(查看)模型,但它应包含一个boolean属性,表明它是否已被选中

public class Role
{
  public int ID { get; set; }
  public string Name { get; set; }
  public bool IsSelected { get; set; }
}

然后在视图中,使用强类型html助手,以便获得正确的双向模型绑定

@for (int j = 0; j < Model[i].Roles.Count; j++)
{ 
  @Html.HiddenFor(m => m[i].Roles[j].Name) // or better use the ID property
  @Html.CheckBoxFor(m => m[i].Roles[j].IsSelected)
  @Html.LabelFor(m => m[i].Roles[j].IsSelected, Model[i].Roles[j].Name)
}

然后在POST方法中,您的集合将被正确绑定,您可以使用say,

访问所选角色
List<string> selectedRoles = model.Roles.Where(r => r.IsSelected);

注意您可能还想为Role.ID(而不是Role.Name属性)包含隐藏输入,因此您不需要在POST方法foreach循环中执行数据库查找

旁注:您的帖子方法需要

public ActionResult UsersAddToApp(List<AssignUsersViewModel> model)

不是ListAssignUsersAddModel> model

答案 1 :(得分:1)

很好的接受答案 - 这是关于复选框和装订的附加说明。

如果查看CheckBoxFor的输出,您会看到字段中有两个输入,例如:

@Html.CheckBoxFor(x=>x.Flag)

给出

<input type='checkbox' name='Flag' ..              // value etc based existing value
<input type='hidden' name='Flag' value='false' />  // always false

这是因为未发送的复选框不会包含在帖子中,因此第二个隐藏字段会向控制器提供false(未标记)值。

当您手动创建input时,您也可以添加此hidden field,然后它将返回false值,然后模型绑定器应该按照您原先的预期进行拾取:

<input type="checkbox" name="[@i.ToString()].selectedRoles[@j.ToString()].Name" value="@Model[i].Roles[j].Name" class="checkbox-inline" />
<input type="hidden" name="[@i.ToString()].selectedRoles[@j.ToString()].Name" value="@Model[i].Roles[j].Name" value='false' />

问题的情况下,我肯定会根据接受的答案重构和使用Html助手。

在其他情况下,可以通过javascript动态添加复选框,因此上面隐藏的输入字段是可行的。

答案 2 :(得分:0)

我发现IEnumerable没有按顺序工作(好消息)

儿子我将复选框的名称更改为:

  • 命名=&#34; [@ i.ToString()] selectedRoles.Name&#34;

然后在控制器中,对于每一个可枚举的项目,我都可以得到以下内容

  • HttpContext.Request.Params [&#34; [1] .selectedRoles.Name&#34;]

返回(以逗号分隔)带有所选项名称的字符串。

到目前为止,我没有其他想法,但它适用于我...我只需要在name属性中限制逗号。