【问题标题】:Steve Sanderson's BeginCollectionItem helper won't bind correctlySteve Sanderson 的 BeginCollectionItem 助手无法正确绑定
【发布时间】:2012-01-23 23:40:24
【问题描述】:

我正在使用 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)
    }
}

我正在使用 jquery 通过 serializeArray() 提交数据。当它进入服务器时,它会正确绑定所有动态的,甚至将保修绑定到集合(集合计数为 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>

【问题讨论】:

  • 您的 foreach 保修在 Html.BeginForm 内?您介意为一组保修发布一些示例 HTML 输出吗?
  • 是的,它们在 Html.BeginForm 中。我想我想通了(部分)。如果我将 foreach 循环更改为 forloop 然后执行类似 @Html.TextBoxFor(x => Model.WarrantyFeaturesVm[i].FeatureId) 的操作。不知道为什么 beginCollection 不起作用。
  • 模型绑定器查看 HTML。查看两种不同情况下 HTML id 呈现方式的差异。我们经常将 foreach 与 BeginCollectionItem 一起使用,但 BeginCollectionItem 通常位于部分模板或编辑器模板中。
  • 所以我做了测试。名字有点奇怪。
  • 您是否尝试过使用 Html.HiddenFor(war => war.FeatureId) 而不是 Html.HiddenFor(m => war.FeatureId)?

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


【解决方案1】:

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

在您执行 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 和名称中出现“战争”。模型绑定器正在寻找它找到的名为“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" .../>

在第三种情况下,情况类似。它正在寻找它找到的 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" .../>

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

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

更新

我挖出了this old post from 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>

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

【讨论】:

  • 啊。我得试试这个。是的,我只是要使用基于整数的方式,但起初我认为帮助者以某种方式影响了当我真的做错了哈哈。
  • BeginCollectionItem 不完美。这是我 10 年前做 php 时唯一怀念的事情。如果 MVC 允许您仅使用输入 id="CollectionProperty[]" 传入集合,我会很高兴。会容易得多。
猜你喜欢
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
  • 1970-01-01
相关资源
最近更新 更多