Steve Sanderson 的 BeginCollectionItem 助手无法正确绑定 [英] Steve Sanderson's BeginCollectionItem helper won't bind correctly

查看:19
本文介绍了Steve Sanderson 的 BeginCollectionItem 助手无法正确绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using Steve Sanderson's BeginCollectionItem helper and ran into a problem. I have a form that has an option to add unlimited reward fields. I am using his helper since it solved this problem with how to keep generating the fields and not have to worry about how to bind it when the form gets submitted.

我在同样的表格中有一些复选框,其中有一个未知的数量.与奖励的区别在于,未知数量将在数据库调用后已知,并在代码进入视图时已知.

I have in this same form some checkboxes that there is an unknown amount. The difference with this one versus the rewards is the unknown amount will become known after a database call and will be known by the time the code gets to the view.

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

So my code looks like this

  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() 提交数据.当它到达服务器时,它正确绑定所有动态,甚至将保修绑定到集合(集合计数为 1).但它从不绑定 WarrantyFeaturesVm 内的任何内容,一切都保留为默认值.

I am using jquery to submit the data by using serializeArray(). When it goes to the server it bind all the dynamic ones correctly and even binds the Warranty to the Collection(the collection count is 1). Yet it never binds anything insides the WarrantyFeaturesVm, everything is left as default.

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

if I remove using (Html.BeginCollectionItem("WarrantyFeaturesVm")) then it won't even bind the collection.

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

Anyone know why it is not binding anything in the collection?

编辑

// 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>

推荐答案

好的,我想我明白这里发生了什么.

Ok I think I see what is going on here.

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

In the second sample, where you did the foreach, it looks like your cshtml was something like this (@ symbols may be incorrect):

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"结尾的原因.模型绑定器正在寻找一个名为WarrantyFeaturesVm"的集合属性,它找到了.然而,它随后在 WarrantyFeaturesVm 视图模型上寻找名为war"的属性,但它找不到,因此不会绑定.

Because BeginCollectionItem uses its context to derive the HTML names and id's, this is why you end up with "war" in the id's and names. The model binder is looking for a collection property named "WarrantyFeaturesVm", which it finds. However it is then looking for a property named "war" on the WarrantyFeaturesVm viewmodel, which it cannot find, and thus does not 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" .../>

在第三种情况下,情况类似.它正在寻找它找到的 WarranyFeaturesVm 集合属性.然而,它会寻找另一个集合项.

In the 3rd scenario, it is similar. It is looking for the WarranyFeaturesVm collection property, which it finds. It however looks for another collection item.

<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" .../>

为了正确绑定,您的 HTML 必须与您的第一个 HTML 示例相似:

In order to bind correctly, your HTML has to look similar to your first HTML example:

<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 及其包装的所有内容放入局部视图来实现这一点.然后,局部视图将接收其自己的上下文,因为您的助手将使用视图的 @Model 属性和强类型助手,如下所示:@Html.WidgetFor(m => m.PropertyName).

Like I hinted in my comment, you can achieve this by putting the BeginCollectionItem and everything it wraps into a partial view. The partial view will then receive its own context, since your helpers will use the view's @Model property with the stongly-typed helpers like so: @Html.WidgetFor(m => m.PropertyName).

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

On the other hand, if you really need the collection to be rendered in the outer view, I don't see any problem using normal indexing (integer-based) with a for loop and without BeginCollectionItem.

更新

我翻出了 Phil Haack 的这篇旧帖子.摘录:

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

...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 使用此索引方法来确保模型绑定发生.唯一的区别是它使用 Guids 而不是 ints 作为索引器.但是您可以像上面 Phil 的示例那样手动设置任何索引器.

BeginCollectionItem uses this indexing method to make sure the model binding happens. The only difference is it uses Guids instead of ints as the indexer. But you could manually set any indexer like in Phil's example above.

这篇关于Steve Sanderson 的 BeginCollectionItem 助手无法正确绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆