Steve Sanderson的BeginCollectionItem助手无法正确绑定

cho*_*bo2 5 asp.net-mvc html-helper model-binding asp.net-mvc-3

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

我在这个相同的形式有一些复选框,有一个未知的数量.这个与奖励的区别在于数据库调用后将知道未知数量,并且在代码到达视图时将会知道.

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

  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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用jquery使用serializeArray()提交数据.当它进入服务器时,它会正确绑定所有动态的,甚至将保修绑定到Collection(集合计数为1).但它永远不会绑定WarrantyFeaturesVm中的任何内容,一切都保留为默认值.

如果我删除使用(Html.BeginCollectionItem("WarrantyFeaturesVm"))它然后甚至不会绑定集合.

任何人都知道为什么它没有绑定集合中的任何东西?

编辑

// 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>
Run Code Online (Sandbox Code Playgroud)

dan*_*wig 9

好吧我想我知道这里发生了什么.

在第二个示例中,您执行了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)
    }
}
Run Code Online (Sandbox Code Playgroud)

因为BeginCollectionItem使用其上下文来派生HTML名称和id,所以这就是你在id和名称中最终得到"war"的原因.模型绑定器正在查找它找到的名为"WarrantyFeaturesVm"的集合属性.然而,它正在WarrantyFeaturesVm视图模型中查找名为"war"的属性,它无法找到,因此不会绑定.

<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" .../>
Run Code Online (Sandbox Code Playgroud)

在第三种情况下,它是类似的.它正在寻找它找到的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" .../>
Run Code Online (Sandbox Code Playgroud)

为了正确绑定,您的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" .../>
Run Code Online (Sandbox Code Playgroud)

就像我在评论中暗示的那样,您可以通过将BeginCollectionItem及其包装的所有内容放入局部视图中来实现此目的.部分视图将接收自己的上下文,因为您的助手将使用视图的@Model属性与强力类型的助手,如下所示:@Html.WidgetFor(m => m.PropertyName).

另一方面,如果你真的需要在外部视图中呈现集合,我没有看到使用带有for循环和没有BeginCollectionItem的普通索引(基于整数)的任何问题.

更新

从Phil Haack挖出了这个老帖子.摘录:

...通过引入额外的隐藏输入,您可以允许任意索引.在下面的示例中,我们为每个需要绑定到列表的项目提供带有.Index后缀的隐藏输入.每个隐藏输入的名称都是相同的,因此如前所述,这将为模型绑定器提供一个很好的索引集合,以便在绑定到列表时查找.

<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>
Run Code Online (Sandbox Code Playgroud)

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