asp.net-mvc - Steve Sanderson 的 BeginCollectionItem 助手无法正确绑定(bind)

标签 asp.net-mvc asp.net-mvc-3 html-helper model-binding

我正在使用 Steve Sanderson 的 BeginCollectionItem 助手并遇到了问题。我有一个表单,可以选择添加无限奖励字段。我正在使用他的助手,因为它解决了如何继续生成字段的问题,而不必担心在提交表单时如何绑定(bind)它。

我以同样的形式有一些复选框,数量未知。这与奖励的区别在于,未知数量将在数据库调用后变得已知,并且将在代码到达 View 时已知。

所以我的代码看起来像这样

  public class FrmVm
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public bool Active { get; set; }

        public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; } // this is the checkbox ones.
         public IList<RewardVms> RewardVms { get; set; } // this is the dyanmic one that I needed the helper for

        public CbCreditCardFrmVm()
        {
            Active = true;
            WarrantyFeaturesVm = new List<WarrantyFeaturesVm>();
             RewardVms = new List<RewardVms>();
        }
    }


    // view

    @foreach (var tier in Model.RewardVms)
    {
            @Html.Partial("GenerateReward", tier)   // in this partial view in the  BeginCollectionItem                 
     }



 @foreach (var warranties in Model.WarrantyFeaturesVm)
{
    using (Html.BeginCollectionItem("WarrantyFeaturesVm"))
    { 
      <span>@warranties.Name:</span>
      @Html.TextBoxFor(x => warranties.FeatureId)
      @Html.CheckBoxFor(x => warranties.HasFeature)
    }
}

我正在使用 jquery 通过 serializeArray() 提交数据。当它进入服务器时,它会正确绑定(bind)所有动态的,甚至将保修绑定(bind)到集合(集合计数为 1)。然而,它从不绑定(bind) WarrantyFeaturesVm 中的任何内容,所有内容都保留为默认值。

如果我使用 (Html.BeginCollectionItem("WarrantyFeaturesVm")) 删除那么它甚至不会绑定(bind)集合。

任何人都知道为什么它不绑定(bind)集合中的任何内容?

编辑
// for loop (works)
<form method="post" id="" action="" class="ui-formwizard ui-helper-reset ui-widget ui-widget-content ui-corner-all" novalidate="novalidate">

<span id="" class="step ui-formwizard-content ui-helper-reset ui-corner-all" style="display: none;">

<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" name="WarrantyFeaturesVm[0].FeatureId" id="WarrantyFeaturesVm_0__FeatureId" data-val-required="The FeatureId field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default">    <span>Purchase</span>
<input type="checkbox" value="true" name="WarrantyFeaturesVm[0].HasFeature" id="WarrantyFeaturesVm_0__HasFeature" data-val-required="The HasFeature field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default"><input type="hidden" value="false" name="WarrantyFeaturesVm[0].HasFeature" class="ui-wizard-content ui-helper-reset ui-state-default">

</form>




//foreach loop beginItemCollection(does not work)


<form method="post" id="" action="" class="ui-formwizard ui-helper-reset ui-widget ui-widget-content ui-corner-all" novalidate="novalidate">

<span id="" class="step ui-formwizard-content ui-helper-reset ui-corner-all" style="display: inline;">

<input type="hidden" value="68ba9241-c409-4f4b-96da-cce13b127c1e" autocomplete="off" name="WarrantyFeaturesVm.index" class="ui-wizard-content ui-helper-reset ui-state-default">
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].war.FeatureId" id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__war_FeatureId" data-val-required="The FeatureId field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default">            <span>Purchase</span>
<input type="checkbox" value="true" name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].war.HasFeature" id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__war_HasFeature" data-val-required="The HasFeature field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default"><input type="hidden" value="false" name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].war.HasFeature" class="ui-wizard-content ui-helper-reset ui-state-default">

</span>

</form>





//for loop beginItemCollection (does not work)
<form method="post" id="" action="" class="ui-formwizard ui-helper-reset ui-widget ui-widget-content ui-corner-all" novalidate="novalidate">


<span id="" class="step ui-formwizard-content ui-helper-reset ui-corner-all" style="display: none;">

<input type="hidden" value="fe3fbc82-a2df-476d-a15a-dacd841df97e" autocomplete="off" name="WarrantyFeaturesVm.index" class="ui-wizard-content ui-helper-reset ui-state-default">
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" name="WarrantyFeaturesVm[fe3fbc82-a2df-476d-a15a-dacd841df97e].WarrantyFeaturesVm[0].FeatureId" id="WarrantyFeaturesVm_fe3fbc82-a2df-476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__FeatureId" data-val-required="The FeatureId field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default">            <span>Purchase</span>
<input type="checkbox" value="true" name="WarrantyFeaturesVm[fe3fbc82-a2df-476d-a15a-dacd841df97e].WarrantyFeaturesVm[0].HasFeature" id="WarrantyFeaturesVm_fe3fbc82-a2df-476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__HasFeature" data-val-required="The HasFeature field is required." data-val="true" class="ui-wizard-content ui-helper-reset ui-state-default"><input type="hidden" value="false" name="WarrantyFeaturesVm[fe3fbc82-a2df-476d-a15a-dacd841df97e].WarrantyFeaturesVm[0].HasFeature" class="ui-wizard-content ui-helper-reset ui-state-default">

</span>

<span id="adminSettings" class="step ui-formwizard-content ui-helper-reset ui-corner-all" style="display: inline;">

</form>

最佳答案

好的,我想我知道这里发生了什么。

在您执行 foreach 的第二个示例中,您的 cshtml 看起来像这样(@ 符号可能不正确):

foreach (var war in Model.WarrantyFeaturesVm) {
    using (Html.BeginCollectionItem("WarrantyFeaturesVm")) {
        Html.HiddenFor(m => war.FeatureId)
        <span>@Html.DisplayFor(m => war.Name)</span>
        Html.HiddenFor(m => war.HasFeature)
    }
}

因为 BeginCollectionItem 使用它的上下文来派生 HTML 名称和 id,这就是为什么您最终会在 id 和名称中出现“ war ”。模型绑定(bind)器正在寻找它找到的名为“WarrantyFeaturesVm”的集合属性。然而,它随后会在 WarrantyFeaturesVm View 模型上寻找一个名为“war”的属性,但它无法找到该属性,因此不会绑定(bind)。
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" 
    name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].war.FeatureId" 
    id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__war_FeatureId" .../>

在第 3 种情况下,情况类似。它正在寻找它找到的 WarranyFeaturesVm 集合属性。然而,它会寻找另一个收藏品。
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" 
    name="WarrantyFeaturesVm[fe3fbc82-a2df-476d-a15a-dacd841df97e].WarrantyFeaturesVm[0].FeatureId" 
    id="WarrantyFeaturesVm_fe3fbc82-a2df-476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__FeatureId" .../>

为了正确绑定(bind),您的 HTML 必须看起来类似于您的第一个 HTML 示例:
<input type="hidden" value="68ba9241-c409-4f4b-96da-cce13b127c1e" 
    name="WarrantyFeaturesVm.index" .../>
<input type="hidden" value="6aa20677-d367-4e2a-84f0-9fbe00deb191" 
    name="WarrantyFeaturesVm[68ba9241-c409-4f4b-96da-cce13b127c1e].FeatureId" 
    id="WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__FeatureId" .../>

就像我在评论中暗示的那样,您可以通过将 BeginCollectionItem 及其包装的所有内容放入局部 View 中来实现这一点。部分 View 将收到它自己的上下文,因为您的助手将使用 View 的 @Model 属性和严格类型的助手,如下所示:@Html.WidgetFor(m => m.PropertyName).
另一方面,如果您确实需要在外部 View 中呈现集合,我认为使用带有 for 循环且没有 BeginCollectionItem 的普通索引(基于整数)没有任何问题。

更新

我挖了this old post from Phil Haack .摘录:

...by introducing an extra hidden input, you can allow for arbitrary indices. In the example below, we provide a hidden input with the .Index suffix for each item we need to bind to the list. The name of each of these hidden inputs are the same, so as described earlier, this will give the model binder a nice collection of indices to look for when binding to the list.


<form method="post" action="/Home/Create">

    <input type="hidden" name="products.Index" value="cold" />
    <input type="text" name="products[cold].Name" value="Beer" />
    <input type="text" name="products[cold].Price" value="7.32" />

    <input type="hidden" name="products.Index" value="123" />
    <input type="text" name="products[123].Name" value="Chips" />
    <input type="text" name="products[123].Price" value="2.23" />

    <input type="hidden" name="products.Index" value="caliente" />
    <input type="text" name="products[caliente].Name" value="Salsa" />
    <input type="text" name="products[caliente].Price" value="1.23" />

    <input type="submit" />
</form>

BeginCollectionItem 使用此索引方法来确保发生模型绑定(bind)。唯一的区别是它使用 Guids 而不是 int 作为索引器。但是您可以手动设置任何索引器,就像上面 Phil 的示例一样。

关于asp.net-mvc - Steve Sanderson 的 BeginCollectionItem 助手无法正确绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8568160/

相关文章:

javascript - Kendo MVC 条形图/柱形图 CategoryAxis Fit day

javascript - cultureinfo 问题 - 添加语言

asp.net - MVC 模型未更新

asp.net-mvc - MVC Controller 中的单一职责原则。需要批评

c# - 使用斜杠定义 ASP.NET MVC 路由

javascript - 在 asp.net mvc 中使用 RTL 输入的货币/小数输入

c# - 如何编写自定义@Html.Control For(o => o.Property)?

asp.net - 一种避免从 mvc 身份验证中的提供者类派生的方法

asp.net-mvc-3 - 这段代码有什么意义?

c# - ASP.NET MVC 2 - 如何从下拉列表中获取选定的值到我的 View 模型中?