史蒂夫·桑德森的BeginCollectionItem助手将无法正确绑定 [英] Steve Sanderson's BeginCollectionItem helper won't bind correctly

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

问题描述

我使用的史蒂夫·桑德森的BeginCollectionItem帮手,并遇到了一个问题。我有有一个选项可以增添无限的奖励字段的表格。我使用他的帮手,因为它解决了如何使生成的领域,没有这个问题不用担心当表单被提交如何绑定。

我在这个相同的形式一些复选框,有一个未知量。与此一对奖励的差是未知的量将成为一个数据库调用后已知的,并且将由code到达该视图的时间是已知的。

所以,我的code看起来像这样

 公共类FrmVm
    {
        公众的Guid标识{搞定;组; }
        公共字符串名称{;组; }
        公共BOOL活跃{搞定;组; }        公众的IList< WarrantyFeaturesVm> WarrantyFeaturesVm {搞定;组; } //这是复选框的。
         公众的IList< RewardVms> RewardVms {搞定;组; } //这是dyanmic一个,我需要帮手        公共CbCreditCardFrmVm()
        {
            活动=真;
            WarrantyFeaturesVm =新的List< WarrantyFeaturesVm>();
             RewardVms =新的List< RewardVms>();
        }
    }
    //视图    @foreach(在Model.RewardVms VAR层)
    {
            @ Html.Partial(GenerateReward层)//在BeginCollectionItem这个局部视图
     } @foreach(在Model.WarrantyFeaturesVm VAR担保)
{
    使用(Html.BeginCollectionItem(WarrantyFeaturesVm))
    {
      &所述;跨度> @ warranties.Name:或其可/跨度>
      @ Html.TextBoxFor(X => warranties.FeatureId)
      @ Html.CheckBoxFor(X => warranties.HasFeature)
    }
}

我使用jquery通过serializeArray提交数据()。当它关系到服务器时,它正确地绑定所有动态的,甚至结合保修到Collection(集合数为1)。然而,它从来没有任何绑定的内心WarrantyFeaturesVm,一切都保留为默认值。

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

任何人都知道为什么它无法在集合中结合东西吗?

修改

  // for循环(作品)
<形式方法=邮报ID =行动=级=UI-formwizard UI辅助复位UI的小部件的UI控件内容UI的角落所有的NOVALIDATE =NOVALIDATE><跨度ID =级=一步UI-formwizard内容UI辅助复位UI的角落都的风格=显示:无;><输入类型=隐藏值=6aa20677-d367-4e2a-84f0-9fbe00deb191NAME =WarrantyFeaturesVm [0] .FeatureIdID =WarrantyFeaturesVm_0__FeatureId数据-VAL-所需=是必需的FEATUREID场。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认> <跨度>购买< / SPAN>
<输入类型=复选框VALUE =真NAME =WarrantyFeaturesVm [0] .HasFeatureID =WarrantyFeaturesVm_0__HasFeature数据-VAL-所需=是必需的HasFeature场。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认><输入类型=隐藏值=假名称=WarrantyFeaturesVm [0] .HasFeature 阶级=UI的向导内容UI辅助复位UI状态默认>< /表及GT;
// foreach循环beginItemCollection(不工作)
<形式方法=邮报ID =行动=级=UI-formwizard UI辅助复位UI的小部件的UI控件内容UI的角落所有的NOVALIDATE =NOVALIDATE><跨度ID =级=一步UI-formwizard内容UI辅助复位UI的角落都的风格=显示:内联;><输入类型=隐藏值=68ba9241-c409-4f4b-96da-cce13b127c1e自动完成=关闭NAME =WarrantyFeaturesVm.index级=UI的向导内容UI辅助复位UI的语句默认的>
<输入类型=隐藏值=6aa20677-d367-4e2a-84f0-9fbe00deb191NAME =WarrantyFeaturesVm [68ba9241-c409-4f4b-96da-cce13b127c1e] .war.FeatureIdID =WarrantyFeaturesVm_68ba9241-c409-4f4b- 96da-cce13b127c1e__war_FeatureId数据-VAL-所需=是必需的FEATUREID场。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认> <跨度>购买< / SPAN>
<输入类型=复选框VALUE =真NAME =WarrantyFeaturesVm [68ba9241-c409-4f4b-96da-cce13b127c1e] .war.HasFeatureID =WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__war_HasFeature数据val-所需=该HasFeature字段是必需的。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认><输入类型=隐藏值=假名称=WarrantyFeaturesVm [68ba9241-c409- 4f4b-96da-cce13b127c1e] .war.HasFeature级=UI的向导内容UI辅助复位UI状态默认>< / SPAN>< /表及GT;// for循环beginItemCollection(不工作)
<形式方法=邮报ID =行动=级=UI-formwizard UI辅助复位UI的小部件的UI控件内容UI的角落所有的NOVALIDATE =NOVALIDATE>
<跨度ID =级=一步UI-formwizard内容UI辅助复位UI的角落都的风格=显示:无;><输入类型=隐藏值=fe3fbc82-a2df-476d-a15a-dacd841df97e自动完成=关闭NAME =WarrantyFeaturesVm.index级=UI的向导内容UI辅助复位UI的语句默认的>
<输入类型=隐藏值=6aa20677-d367-4e2a-84f0-9fbe00deb191NAME =WarrantyFeaturesVm [fe3fbc82-a2df-476d-a15a-dacd841df97e] .WarrantyFeaturesVm [0] .FeatureIdID =WarrantyFeaturesVm_fe3fbc82-a2df -476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__FeatureId数据-VAL-所需=是必需的FEATUREID场。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认> <跨度>购买< / SPAN>
<输入类型=复选框VALUE =真NAME =WarrantyFeaturesVm [fe3fbc82-a2df-476d-a15a-dacd841df97e] .WarrantyFeaturesVm [0] .HasFeatureID =WarrantyFeaturesVm_fe3fbc82-a2df-476d-a15a-dacd841df97e__WarrantyFeaturesVm_0__HasFeature数据-val要求的=是必需的HasFeature场。数据-VAL =真正的类=UI的向导内容UI辅助复位UI状态默认><输入类型=隐藏值=假名称=WarrantyFeaturesVm [fe3fbc82-a2df- 476d-a15a-dacd841df97e] .WarrantyFeaturesVm [0] .HasFeature级=UI的向导内容UI辅助复位UI状态默认>< / SPAN><跨度ID =adminSettings级=一步UI-formwizard内容UI辅助复位UI的角落都的风格=显示:内联;>< /表及GT;


解决方案

好吧,我想我明白是怎么回事。

在第二个样本,在那里你没有在foreach,它看起来像你的CSHTML是像这样(@符号可能是不正确的):

 的foreach(在Model.WarrantyFeaturesVm VAR战争){
    使用(Html.BeginCollectionItem(WarrantyFeaturesVm)){
        Html.HiddenFor(M => war.FeatureId)
        &所述;跨度> @ Html.DisplayFor(M => war.Name)所述; /跨度>
        Html.HiddenFor(M => war.HasFeature)
    }
}

由于BeginCollectionItem使用它的上下文推导HTML姓名和身份证的,这就是为什么你最终的ID和姓名的战争。该模型粘合剂寻找一个名为WarrantyFeaturesVm集合属性,它发现。然而,然后找一个名为战争的WarrantyFeaturesVm视图模型,它无法找到,因此不绑定属性。

 <输入类型=隐藏值=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集合属性,它发现。然而,它会寻找另一个集合项目​​。

 <输入类型=隐藏值=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例子:

 <输入类型=隐藏值=68ba9241-c409-4f4b-96da-cce13b127c1e
    NAME =WarrantyFeaturesVm.index... />
<输入类型=隐藏值=6aa20677-d367-4e2a-84f0-9fbe00deb191
    NAME =WarrantyFeaturesVm [68ba9241-c409-4f4b-96da-cce13b127c1e] .FeatureId
    ID =WarrantyFeaturesVm_68ba9241-c409-4f4b-96da-cce13b127c1e__FeatureId... />

就像我在我的评论暗示,你可以通过将BeginCollectionItem和它包装成一个局部视图一切实现这一目标。然后,局部视图将收到自己的上下文,因为你的助手将使用视图的@Model属性与stongly类型的助手,像这样: @ Html.WidgetFor(M = GT; m.PropertyName)

在另一方面,如果你真的需要在外部视图呈现的收藏,我看不出有(整数型)使用普通索引的任何问题,for循环,无BeginCollectionItem。

更新

我挖出了这个老职位从菲尔哈克 。摘录:


  

...通过引入一个额外的隐藏的输入,可以允许任意
  指数。在下面的例子中,我们提供了一个隐藏的输入
  后缀的.index为每个需要绑定到列表项。的名称
  每个这些隐藏的输入是一样的,所以如前所述,
  这将给模型绑定指数的一个很好的集合看
  用于结合到列表时


 <形式方法=邮报行动=/首页/新建>    <输入类型=隐藏的名字=products.IndexVALUE =冷/>
    <输入类型=文本名称=产品[感冒] .Name点值=啤酒/>
    <输入类型=文本名称=产品[感冒]。价格值=7.32/>    <输入类型=隐藏的名字=products.IndexVALUE =123/>
    <输入类型=文本名称=产品[123] .Name点值=筹码/>
    <输入类型=文本名称=产品[123]。价格值=2.23/>    <输入类型=隐藏的名字=products.IndexVALUE =克莱恩特/>
    <输入类型=文本名称=产品[克莱恩特] .Name点值=莎莎/>
    <输入类型=文本名称=产品[克莱恩特]。价格值=1.23/>    <输入类型=提交/>
< /表及GT;

BeginCollectionItem使用该索引方法,以确保该模型结合发生。唯一的区别是它使用代替整数作为索引的GUID。但是,你可以手动设置任何索引如上面菲尔的例子。

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

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.

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?

Edit

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

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

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

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

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

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).

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.

Update

I dug up this old post from Phil Haack. An excerpt:

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

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

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