【问题标题】:MVC3 - Viewmodel with list of complex typesMVC3 - 具有复杂类型列表的视图模型
【发布时间】:2012-07-25 13:02:26
【问题描述】:

如果之前有人问过这个问题,我们深表歉意;有一百万种表达方式,因此很难找到答案。

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

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; }
}

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

@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 为空。

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

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

【问题讨论】:

  • 您能向我们展示您正在填充数据模型的控制器代码吗?
  • +1 表示涵盖该领域的最优雅描述的问题。相信我,我们在过去 2 个小时里一直在寻找这个问题!

标签: asp.net-mvc-3 viewmodel model-binding checkboxfor


【解决方案1】:

我的假设是这种情况正在发生,因为即使我 编辑其内容,Devices 属性永远不会显式 包含在表单中。

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

但是为什么会这样呢?

这是因为您在视图中使用了 foreach 循环。您使用x =&gt; 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&lt;T&gt; 或数组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>
}

【讨论】:

  • 很好的答案,但您为什么不建议在视图模型中使用索引集合?
  • @JamieIde,因为我更喜欢使用编辑器模板。当框架已经考虑到这一点时,为什么还要在视图中编写循环?
  • @DarinDimitrov 我明白你的意思,我想知道这是个人喜好还是其他原因。有时我觉得模板包含部分 html(例如在您的示例中表示表格行)或 jQuery 等在页面中引用的元素。效果是模板只能在一个页面上使用,所以为什么要打破它?
  • @JamieIde,我想这真的是个人选择的问题。
  • +1。经过 2 小时的观察和尝试,它是互联网上唯一的一篇文章。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 2011-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多