且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

MVC3 - 视图模型复杂类型列表

更新时间:2022-10-14 22:47:25


  

我的假设是,这种情况正在发生,因为即使我
  编辑其内容的设备属性是从未明确
  包括在形式


块引用>

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


  

但是,为什么出现这种情况?


块引用>

这是因为您在视图中使用的的foreach 循环。你用 X => device.IsSelected 为lambda前pression的复选框,但这并不考虑设备的属性在所有(你可以通过查看生成的源$ C ​​$ C看到你网页)。


  

那么,应该怎么办?


块引用>

我个人使用编辑模板,因为他们尊重复杂属性的导航上下文,并产生正确的输入名称推荐你。因此,获得在你看来摆脱了整个的foreach 循环,用code一行替换为:

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

和现在定义将自动ASP.NET MVC的设备集合中的每个元素呈现一个自定义编辑器模板。警告:此模板的位置和名称都为这个非常重要的工作原理是约定:〜/查看/共享/ EditorTemplates / SelectableDeviceViewModel.cshtml

  @model SelectableDeviceViewModel
@ Html.HiddenFor(X => x.DeviceInstanceId)
&所述; TR>
    < TD> @ Html.CheckBoxFor(X => x.IsSelected)LT; / TD>
    < TD> @ Html.DisplayFor(X => x.Name)LT; / TD>
< / TR>


另一种方法(我不建议)是改变你目前的的ICollection 在您的视图模式向的索引的集合(如一个的IList< T> 或数组 T []

 公共类AssignSoftwareLicenseViewModel
{
    公众诠释LicenseId {搞定;组; }
    公众的IList< SelectableDeviceViewModel>设备{搞定;组; }
}

然后,而不是在foreach使用循环:

  @for(VAR I = 0; I< Model.Devices.Count;我++)
{
    @ Html.HiddenFor(X => x.Devices [I] .DeviceInstanceId)
    &所述; TR>
        < TD> @ Html.CheckBoxFor(X => x.Devices [I] .IsSelected)LT; / TD>
        < TD> @ Html.DisplayFor(X => x.Devices [I] .Name点< / TD>
    < / TR>
}

Apologies if this has been asked before; there are a million ways to phrase it so searching for an answer has proved difficult.

I have a viewmodel with the following properties:

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

A simplified version of SelectableDeviceViewModel would be this:

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

In my View, I am attempting to display a list of editable checkboxes for the Devices property, inside an input form. Currently, my View looks like this:

@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" />
}

The problem is, when the model gets posted back to the controller, Devices is null.

My assumption is that this is happening because even though I'm editing its contents, the Devices property is never explicitly included in the form. I tried including it with HiddenFor, but that just resulted in the model having an empty list instead of null.

Any idea what I'm doing wrong here?

My assumption is that this is happening because even though I'm editing its contents, the Devices property is never explicitly included in the form.

No, your assumption is wrong. The reason this doesn't get bound properly is because your input fields doesn't have correct names. For example they are called name="IsSelected" instead of name="Devices[0].IsSelected". Take a look at the correct wire format that needs to be used to bind to collections: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

But why this happens?

It happens because of the foreach loop that you used in your view. You used x => device.IsSelected as lambda expression for the checkbox but this doesn't take into account the Devices property at all (as you can see by looking at the generated source code of your web page).

So what should I do?

Personally I would recommend you using editor templates as they respect the navigational context of complex properties and generate correct input names. So get rid of the entire foreach loop in your view and replace it with a single line of code:

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

and now define a custom editor template that will automatically be rendered by ASP.NET MVC for each element of the Devices collection. Warning: the location and name of this template are very important as this works by convention: ~/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>


Another approach (which I don't recommend) is to change your current ICollection in your view model to an indexed collection (such as an IList<T> or an array T[]):

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

and then instead of the foreach use a for loop:

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