2011-12-19 9 views
5

Używam helpera BeginCollectionItem Steve Sandersona i wystąpił problem. Mam formularz, który ma opcję dodania nieograniczonej liczby pól nagrody. Używam jego pomocnika, gdyż rozwiązał ten problem ze jak zachować generowania pola i nie trzeba się martwić, jak związać go, gdy formularz zostanie przesłany.Pomocnik Steve'a Sandersona BeginCollectionItem nie będzie poprawnie wiązał się

mam w tej samej formie kilka pól wyboru, że nie jest nieznany kwota. Różnica w porównaniu z tym jedną z nagród jest nieznany kwota będzie znana po zakończeniu połączenia bazy danych i będą znane w momencie kod dostaje się do widzenia.

Więc mój kod wygląda następująco

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

używam jQuery do przekazywania danych za pomocą serializeArray(). Gdy dojdzie do serwera to wiązać wszystkie dynamicznych poprawnie i nawet wiąże gwarancji do kolekcji (liczba kolekcja jest 1). Mimo to nigdy nie wiąże się z niczym, co ma cechy WarrantyFeaturesVm, wszystko pozostaje domyślnie.

jeśli usuniemy za pomocą (Html.BeginCollectionItem("WarrantyFeaturesVm")), to nawet nie będzie wiązać kolekcji.

Każdy wie, dlaczego to nie jest nic wiążącego w kolekcji?

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> 
+0

A twoje gwarancje foreach znajdują się wewnątrz Html.BeginForm? Czy mógłbyś opublikować przykładowy wynik HTML dla zestawu gwarancji? – danludwig

+0

Tak, są one w Html.BeginForm. Myślę, że to wymyśliłem (częściowo). Jeśli zmienię pętlę foreach na forloop, zrób coś takiego jak @ Html.TextBoxFor (x => Model.WarrantyFeaturesVm [i] .FeatureId) to działa. Nie wiem, dlaczego beginCollection nie działa. – chobo2

+0

Spoiwo modelu wygląda na HTML. Sprawdź różnice w sposobie renderowania identyfikatorów HTML w 2 różnych przypadkach. Często używamy foreach z BeginCollectionItem, ale często BeginCollectionItem jest w szablonie częściowym lub edytorze. – danludwig

Odpowiedz

8

Ok myślę, widzę, co się tu dzieje.

W drugiej próbie, której udało się foreach, wygląda Twój cshtml było coś takiego (@ symbole mogą być błędne):

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

Ponieważ BeginCollectionItem wykorzystuje swój kontekst w celu uzyskania nazwy HTML i id's, dlatego kończy się na "war" w id i imiona. Segregator modelu szuka właściwości kolekcji o nazwie "WarrantyFeaturesVm", która znajduje. Jednak wówczas szuka właściwości o nazwie "war" w viewmodel WarrantyFeaturesVm, której nie może znaleźć, a zatem nie wiąże.

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

W trzecim scenariuszu jest podobnie. Poszukuje właściwości kolekcji WarranyFeaturesVm, którą znajdzie. Szuka jednak innego elementu kolekcji.

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

W celu związania prawidłowo, HTML musi wyglądać podobnie do pierwszego przykładu 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" .../> 

Jak wspomniałem w moim komentarzu, można to osiągnąć poprzez umieszczenie BeginCollectionItem i wszystko, zawija się w częściowy widok. Widok częściowy otrzyma wtedy swój własny kontekst, ponieważ Twoi pomocnicy będą używać właściwości @Model widoku z pomocnikami o nieskomplikowanych typach: @Html.WidgetFor(m => m.PropertyName).

Z drugiej strony, jeśli naprawdę potrzebujesz, aby kolekcja była renderowana w widok zewnętrzny, nie widzę żadnego problemu przy użyciu normalnego indeksowania (opartego na liczbach całkowitych) z pętlą for i bez BeginCollectionItem.

Aktualizacja

I wykopali this old post from Phil Haack. Fragment:

... wprowadzając dodatkowe ukryte dane wejściowe, można zezwolić na dowolne indeksy . W poniższym przykładzie udostępniamy ukryte dane wejściowe z sufiksem .Index dla każdego elementu, który musimy powiązać z listą. Nazwa każdego z tych ukrytych wejść jest taka sama, tak jak opisano wcześniej, da to modelowi spinaczowi ładną kolekcję indeksów wyglądających na dla wiązania z 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 używa tej metody indeksowania, aby upewnić się model wiążący się dzieje. Jedyna różnica polega na tym, że używa Guids zamiast ints jako narzędzia indeksującego. Ale możesz ręcznie ustawić dowolny indeksator, jak w powyższym przykładzie Phila.

+0

Ah. Będę musiał to wypróbować. Ja po prostu zamierzam użyć metody opartej na liczbach całkowitych, ale początkowo myślałem, że w jakiś sposób pomocnik działa, kiedy naprawdę robię to po prostu źle. – chobo2

+0

BeginCollectionItem nie jest idealny. To jedyna rzecz, za którą tęsknię, robiąc php 10 lat temu. Byłbym zachwycony, gdyby MVC pozwoliło ci przekazać kolekcje po prostu używając input id = "CollectionProperty []". Byłoby o wiele łatwiejsze. – danludwig

Powiązane problemy