MVC3-具有复杂类型列表的视图模型



如果之前有人问过这个问题,我们深表歉意;有一百万种表达方式,因此寻找答案已被证明是困难的。

我有一个具有以下属性的视图模型:

public class AssignSoftwareLicenseViewModel
{
    public int LicenseId { get; set; }
    public ICollection<SelectableDeviceViewModel> Devices { get; set; }
}

SelectableDeviceViewModel的简化版本如下:

public class SelectableDeviceViewModel
{
    public int DeviceInstanceId { get; set; }
    public bool IsSelected { get; set; }
    public string Name { get; set; }
}

在我的视图中,我试图在输入表单中显示"设备"属性的可编辑复选框列表。目前,我的视图如下:

@using (Html.BeginForm())
{
    @Html.HiddenFor(x => Model.LicenseId)
    <table>
        <tr>
            <th>Name</th>
            <th></th>
        </tr>
        @foreach (SelectableDeviceViewModel device in Model.Devices)
        {
            @Html.HiddenFor(x => device.DeviceInstanceId)
            <tr>
                <td>@Html.CheckBoxFor(x => device.IsSelected)</td>
                <td>@device.Name</td>
            </tr>
        }
    </table>
    <input type="submit" value="Assign" />
}

问题是,当模型被发布回控制器时,Devices为null。

我的假设是,之所以会发生这种情况,是因为即使我正在编辑其内容,Devices属性也从未明确包含在表单中。我尝试将其包含在HiddeFor中,但这只会导致模型的列表为空,而不是null。

知道我在这里做错了什么吗?

我的假设是,这是因为即使我编辑其内容时,"设备"属性从不显式包含在表单中。

不,你的假设是错误的。无法正确绑定的原因是您的输入字段没有正确的名称。例如,它们被称为name="IsSelected"而不是name="Devices[0].IsSelected"。查看绑定到集合时需要使用的正确连线格式:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

但为什么会发生这种情况呢?

发生这种情况是因为您在视图中使用了foreach循环。您使用x => device.IsSelected作为复选框的lambda表达式,但这根本没有考虑Devices属性(通过查看生成的网页源代码可以看到)。

那么我该怎么办呢?

就我个人而言,我建议您使用编辑器模板,因为它们尊重复杂属性的导航上下文并生成正确的输入名称。因此,在您的视图中去掉整个foreach循环,并用一行代码替换它:

@Html.EditorFor(x => x.Devices)

现在定义一个自定义编辑器模板,该模板将由ASP.NET MVC为Devices集合的每个元素自动呈现。警告:此模板的位置和名称非常重要,因为它按惯例工作:~/Views/Shared/EditorTemplates/SelectableDeviceViewModel.cshtml:

@model SelectableDeviceViewModel
@Html.HiddenFor(x => x.DeviceInstanceId)
<tr>
    <td>@Html.CheckBoxFor(x => x.IsSelected)</td>
    <td>@Html.DisplayFor(x => x.Name)</td>
</tr>

另一种方法(我不建议)是将视图模型中当前的ICollection更改为索引的集合(例如IList<T>或数组T[]):

public class AssignSoftwareLicenseViewModel
{
    public int LicenseId { get; set; }
    public IList<SelectableDeviceViewModel> Devices { get; set; }
}

然后使用for循环代替foreach:

@for (var i = 0; i < Model.Devices.Count; i++)
{
    @Html.HiddenFor(x => x.Devices[i].DeviceInstanceId)
    <tr>
        <td>@Html.CheckBoxFor(x => x.Devices[i].IsSelected)</td>
        <td>@Html.DisplayFor(x => x.Devices[i].Name</td>
    </tr>
}

EditorFor模板工作并保持代码干净。您不需要循环,并且模型已正确发布。

但是,是否有人在验证复杂视图模型(嵌套EditorFor模板)时遇到问题?我正在使用Kendo Validator,遇到了各种jquery错误。

最新更新