2011-12-19 20 views
5

Estoy usando el asistente BeginCollectionItem de Steve Sanderson y me encontré con un problema. Tengo un formulario que tiene la opción de agregar campos de recompensa ilimitados. Estoy usando su ayudante ya que resolvió este problema con la forma de seguir generando los campos y no tener que preocuparse por cómo vincularlo cuando se envía el formulario.El asistente BeginCollectionItem de Steve Sanderson no enlazará correctamente

Tengo en esta misma forma algunas casillas que hay una cantidad desconocida. La diferencia con esta contra las recompensas es que la cantidad desconocida se conocerá después de una llamada a la base de datos y se conocerá cuando el código llegue a la vista.

Así que mi código es el siguiente

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

estoy usando jQuery para enviar los datos mediante el uso de serializeArray(). Cuando va al servidor, vincula todos los dinámicos correctamente e incluso vincula la Garantía a la Colección (el recuento de la colección es 1). Sin embargo, nunca se une nada dentro de WarrantyFeaturesVm, todo queda como predeterminado.

si elimino usando (Html.BeginCollectionItem("WarrantyFeaturesVm")), entonces ni siquiera enlazará la colección.

¿Alguien sabe por qué no es vinculante para nada en la colección?

Editar

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

¿Y sus garantías foreach están dentro de un Html.BeginForm? ¿Le importaría publicar alguna muestra de salida HTML para un conjunto de garantías? – danludwig

+0

Sí, están en un Html.BeginForm. Creo que lo descubrí (parcialmente). Si cambio el bucle foreach a un bucle forloop, entonces haga algo como @ Html.TextBoxFor (x => Model.WarrantyFeaturesVm [i] .FeatureId) funciona. No estoy seguro de por qué el beginCollection no funciona bien. – chobo2

+0

El archivador modelo mira el HTML. Mira las diferencias en cómo los identificadores de HTML se procesan de manera diferente en los 2 casos diferentes. Utilizamos foreach a menudo con BeginCollectionItem, pero a menudo el BeginCollectionItem está en una plantilla parcial o de editor. – danludwig

Respuesta

8

Ok me parece ver lo que está pasando aquí.

En la segunda muestra, donde se hizo el foreach, parece que su cshtml era algo como esto (@ símbolos pueden ser incorrectos):

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

Debido BeginCollectionItem utiliza su contexto para derivar los nombres HTML y Identificación, esta es la razón por la que terminas con "guerra" en los identificadores y nombres. El archivador modelo está buscando una propiedad de colección llamada "WarrantyFeaturesVm", que encuentra. Sin embargo, está buscando una propiedad denominada "guerra" en el modelo de vista WarrantyFeaturesVm, que no puede encontrar y, por lo tanto, no enlaza.

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

En el tercer escenario, es similar. Está buscando la propiedad de la colección WarranyFeaturesVm, que encuentra. Sin embargo, busca otro elemento de colección.

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

Con el fin de unir correctamente, el HTML tiene que ser similar al primer ejemplo de 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" .../> 

Como he indicado en mi comentario, se puede lograr esto poniendo el BeginCollectionItem y todo lo se envuelve en una vista parcial. La vista parcial recibirá entonces su propio contexto, ya que sus ayudantes usarán la propiedad @Model de la vista con los ayudantes de tipo stoutly like: @Html.WidgetFor(m => m.PropertyName).

Por otro lado, si realmente necesita que la colección se represente en el vista externa, no veo ningún problema al utilizar la indexación normal (basada en un entero) con un ciclo for y sin BeginCollectionItem.

actualización

Desenterré this old post from Phil Haack. Un extracto:

... al introducir una entrada oculta adicional, puede permitir índices arbitrarios . En el siguiente ejemplo, proporcionamos una entrada oculta con el . Sufijo de índice para cada elemento que necesitamos vincular a la lista. El nombre de cada una de estas entradas ocultas es la misma, por lo que como se describió anteriormente, esto dará al clasificador de modelos una buena colección de índices para que se vea cuando se enlace a la lista.

<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 utiliza este método de indexación para asegurarse de que ocurra la unión del modelo. La única diferencia es que usa Guids en lugar de Ints como indexador. Pero puede configurar manualmente cualquier indexador como en el ejemplo de Phil anterior.

+0

Ah. Voy a tener que probar esto. Ya solo voy a usar la forma basada en enteros, pero al principio pensé que de alguna manera el ayudante estaba afectando eso cuando realmente solo lo estaba haciendo mal jajaja. – chobo2

+0

BeginCollectionItem no perfecto. Esto es lo único que extraño sobre hacer php hace 10 años. Me encantaría que MVC te dejara pasar colecciones simplemente usando la entrada id = "CollectionProperty []". Sería mucho más fácil. – danludwig

Cuestiones relacionadas