MVC 绑定到具有列表属性的模型忽略其他属性

Posted

技术标签:

【中文标题】MVC 绑定到具有列表属性的模型忽略其他属性【英文标题】:MVC binding to model with list property ignores other properties 【发布时间】:2011-07-05 16:01:55 【问题描述】:

我有一个基本的 ViewModel,它的属性是复杂类型的列表。绑定时,我似乎无法根据发布的值(即视图排列)获取值列表或其他模型属性。

视图模型:

public class MyViewModel

    public int Id  get; set; 
    public string Property1  get; set; 
    public string Property2  get; set; 

    public List<MyDataItem> Data  get; set; 


public class MyDataItem

    public int Id  get; set; 
    public int ParentId  get; set; 
    public string Name  get; set; 
    public string Value  get; set; 

控制器动作:

    public ActionResult MyForm()
    
        MyViewModel model = new MyViewModel();

        model.Id = 1;
        model.Data = new List<MyDataItem>() 
         
            new MyDataItem Id = 1, ParentId = 1, Name = "MyListItem1", Value = "SomeValue"
        ; 

        return View(model);
    

    [HttpPost]
    public ActionResult MyForm(MyViewModel model)
    
        //...

        return View(model);
    

这是基本视图(没有列表标记)

@using (html.BeginForm()) 
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>My View Model</legend>

        @Html.HiddenFor(model => model.Id)

        <div class="editor-label">
            @Html.LabelFor(model => model.Property1)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Property1)
            @Html.ValidationMessageFor(model => model.Property1)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Property2)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Property2)
            @Html.ValidationMessageFor(model => model.Property2)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

当发布回控制器时,我得到了 2 个属性值和 'Data' 属性的空值,如预期的那样。

如果我为列表添加标记如下(基于此Scott Hanselman post 和Phil Haack's post 中的信息):

<div class="editor-field">
@for (int i = 0; i < Model.Data.Count(); i++)

    MyDataItem data = Model.Data[i];
    @Html.Hidden("model.Data[" + i + "].Id", data.Id)
    @Html.Hidden("model.Data[" + i + "].ParentId", data.ParentId)
    @Html.Hidden("model.Data[" + i + "].Name", data.Name)
    @Html.TextBox("model.Data[" + i + "].Value", data.Value)

</div>

模型的“数据”属性已成功绑定,但其他属性为空。

张贴的表单值如下:

Id=1&Property1=test1&Property2=test2&model.Data%5B0%5D.Id=1&model.Data%5B0%5D.ParentId=1&model.Data%5B0%5D.Name=MyListItem1&model.Data%5B0%5D.Value=SomeValue

有没有办法让两组属性都被填充,或者我只是遗漏了一些明显的东西?

编辑:

对于那些好奇的人。根据MartinHN 的回答,原来生成的标记是:

<div class="editor-field">
    <input id="model_Data_0__Id" name="model.Data[0].Id" type="hidden" value="1" />
    <input id="model_Data_0__ParentId" name="model.Data[0].ParentId" type="hidden" value="1" />
    <input id="model_Data_0__Name" name="model.Data[0].Name" type="hidden" value="MyListItem1" />
    <input id="model_Data_0__Value" name="model.Data[0].Value" type="text" value="SomeValue" />        
</div>

新生成的标记是:

<div class="editor-field">
    <input id="Data_0__Id" data-val="true" name="Data[0].Id" type="hidden" value="1" data-val-number="The field Id must be a number." data-val-required="The Id field is required." />
    <input id="Data_0__ParentId" name="Data[0].ParentId" type="hidden" value="1"  data-val="true" data-val-number="The field ParentId must be a number." data-val-required="The ParentId field is required." />
    <input id="Data_0__Name" name="Data[0].Name" type="hidden" value="MyListItem1" />
    <input id="Data_0__Value" name="Data[0].Value" type="text" value="SomeValue" />        
</div>

这会导致以下发布的值:

Id=1&Property1=test1&Property2=test2&Data%5B0%5D.Id=1&Data%5B0%5D.ParentId=1&Data%5B0%5D.Name=MyListItem1&Data%5B0%5D.Value=SomeValue

请注意,没有“模型”。在名称和发布的值中......

【问题讨论】:

不要踩踏踏板或其他任何东西。但是如何更新现有的 public List Data get;放; - 添加一个项目,减去等,然后在客户端更新父模型而不刷新屏幕?如果 Data 为空,并且只能在运行时根据用户对父模型的其他属性的选择知道,那又如何呢?好吧,我想这有点夸张,希望我们朝着同一个方向前进:) 【参考方案1】:

尝试将 Data 集合的代码更改为此,并让 MVC 负责命名:

<div class="editor-field">
@for (int i = 0; i < Model.Data.Count(); i++)

    @Html.HiddenFor(m => m.Data[i].Id)
    @Html.HiddenFor(m => m.Data[i].ParentId)
    @Html.HiddenFor(m => m.Data[i].Name)
    @Html.TextBoxFor(m => m.Data[i].Value)

</div>

【讨论】:

我真的不敢相信我错过了。感谢您的帮助!【参考方案2】:

或者,您可以为嵌套的 ViewModel 创建一个EditorTemplate,如下所示。

@model MyDataItem
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.ParentId)
@Html.HiddenFor(model => model.Name)
@Html.TextBoxFor(model => model.Value)

在您的“Shared”文件夹中创建一个名为“EditorTemplates”的文件夹,并将上述内容另存为“MyDataItem.cshtml”。

然后在您的视图中,只需调用以下代码而不是 foreach 循环:

@Html.EditorFor(model => model.Data)

感觉不太像 IMO :)

【讨论】:

【参考方案3】:

只是我的 2 美分,但这个问题值得注意 - 视图模型中的数据成员必须定义为回发模型绑定工作的公共属性。 我遇到了与上述问题非常相似的问题,但在我的视图模型中使用了公共数据成员。如上所示生成了相同的 HTML,一切看起来都很好,但是模型绑定器返回了一个空集合。值得关注...

【讨论】:

以上是关于MVC 绑定到具有列表属性的模型忽略其他属性的主要内容,如果未能解决你的问题,请参考以下文章

MVC 4如何将模型属性列表从视图传递给Action

将 ListBoxFor 绑定到 MVC 3 中的模型并使用另一个模型自动选择多个项目

绑定到模型列表元素的属性

c# 组合框绑定到对象列表

将列表框项内的命令绑定到视图模型父级上的属性

Spring MVC 和表单绑定:如何从列表中删除项目?