2011-12-19 2 views
5

Steve Sanderson의 BeginCollectionItem 도우미를 사용하고 있는데 문제가 발생했습니다. 무제한 보상 필드를 추가 할 수있는 양식이 있습니다. 필드를 생성하는 방법을 계속 유지하면서 양식을 제출할 때이를 바인딩하는 방법에 대해 걱정할 필요가없는이 문제를 해결 한 이후로 도우미를 사용하고 있습니다.Steve Sanderson의 BeginCollectionItem 도우미가 올바르게 바인딩되지 않습니다.

이 양식에는 알 수없는 금액이있는 체크 박스가 있습니다. 보상과의 차이점은 데이터베이스 호출 후에 알 수없는 양이 알려지고 코드가 뷰에 도달 할 때 알 수 있다는 점입니다.

그래서 내 코드는 내가 serializeArray를 사용하여 데이터를 전송하기 위해 jQuery를 사용하고이

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

()처럼 보인다. 서버로 이동하면 동적 인 모든 것을 올바르게 바인딩하고 보증에 대해서도 콜렉션에 바인드합니다 (콜렉션 수는 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> 
+0

foreach 보증은 Html.BeginForm 내부에 있습니까? 몇 가지 견본 HTML 출력을 보증 세트에 게시 하시겠습니까? – danludwig

+0

예, Html.BeginForm에 있습니다. 나는 그것을 (부분적으로) 이해했다고 생각한다. foreach 루프를 forloop으로 변경하면 @ Html.TextBoxFor (x => Model.WarrantyFeaturesVm [i] .FeatureId)와 같은 작업을 수행 할 수 있습니다. beginCollection이 작동하지 않는 이유는 확실하지 않습니다. – chobo2

+0

모델 바인더는 HTML을 봅니다. 두 가지 경우에서 HTML id가 다르게 렌더링되는 방식의 차이점을 확인하십시오. 우리는 BeginCollectionItem과 함께 종종 foreach를 사용하지만 BeginCollectionItem은 부분 또는 편집기 템플릿에있는 경우가 많습니다. – danludwig

답변

8

이 좋아 내가 여기 무슨 일이 일어나고 있는지 볼 생각합니다.

당신의 cshtml이 같은 (@ 기호는 올바르지 않을 수 있습니다)처럼 당신이, 그것은 foreach 문을 보이는 않았다 두 번째 샘플에서

:

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 이름을 유도하기 위해 컨텍스트를 사용하기 때문에과 이드는이 때문에 이드와 이름에 "전쟁"이 생기는 이유입니다. 모델 바인더는 "WarrantyFeaturesVm"이라는 컬렉션 속성을 찾고 있습니다. 그러나 WarrantyFeaturesVm viewmodel에서 찾을 수없는 "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" .../> 

3 번째 시나리오에서와 유사합니다. 찾은 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 => 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 확실히 모델 바인딩이 발생하려면이 인덱싱 방법을 사용합니다. 유일한 차이점은 인덱서로 int 대신 GUID를 사용한다는 것입니다. 그러나 위의 Phil의 예와 같이 인덱서를 수동으로 설정할 수 있습니다.

+0

아. 나는 이것을 시험해야 할 것이다. 나중에 정수형 방식을 사용 하겠지만, 처음에는 어떻게 든 도우미가 효과가 있었을 것이라고 생각했습니다. – chobo2

+0

BeginCollectionItem이 완벽하지 않습니다. 이것은 내가 10 년 전에 PHP를하는 것에 대해 놓친 유일한 것입니다. MVC를 사용하면 간단하게 입력 ID = "CollectionProperty []"를 사용하여 컬렉션을 전달할 수 있다면 좋을 것입니다. 순전히 더 쉬울 는가 것입니다. – danludwig

관련 문제