2010-10-17 15 views
8

Estoy intentando enlazar una lista que es parte de un modelo de vista más grande sin recurrir a un archivador de modelo personalizado. Cuando uso una plantilla de editor para compilar la lista de entradas, los nombres generados no están en el formato correcto para que funcione la carpeta predeterminada.ASP.NET MVC Model Binding IList en una plantilla de editor

en lugar de artículos [3] .Id como lo esperaría es artículos. [3] .Id. Si construyo la lista sin una plantilla de editor, funciona como se esperaba.

¿Estoy haciendo algo obviamente incorrecto o es esto solo una peculiaridad de Html.Hidden y Html.TextBox?

public class ItemWrapper 
{ 
    [UIHint("ItemList")] 
    public IList<Item> Items { get; set; } 
} 

public class Item 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public int Value { get; set; } 
} 

Index.aspx

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 

    <h2>Index</h2> 

    <% using(Html.BeginForm()) 
    {%> 
    <%:Html.EditorFor(m => m.Items) %> 
    <%}%> 
</asp:Content> 

ItemList.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IList<Mvc2Test.Models.Item>>" %> 

<h4>Asset Class Allocation</h4> 
<% if(Model.Count > 0) { %> 
<table> 
    <tbody> 
    <% for(int i = 0; i < Model.Count; i++) 
    {%> 
     <tr> 
     <td><%: Model[i].Name%></td> 
     <td> 
      <%: Html.HiddenFor(m => m[i].Id) %> 
      <%: Html.TextBoxFor(m => m[i].Value) %> 
     </td> 
     </tr> 
    <%}%> 
    </tbody> 
</table> 
<% 
}%> 

salida

<tr> 
    <td>Item 4</td> 
    <td> 
    <input id="Items__3__Id" name="Items.[3].Id" type="hidden" value="f52a1f57-fca8-4bc5-a746-ee0cef4e05c2" /> 
    <input id="Items__3__Value" name="Items.[3].Value" type="text" value="40" /> 
    </td> 
</tr> 

Editar (Método Acción)

public ActionResult Test() 
{ 
    return View(
    new ItemWrapper 
    { 
     Items = new List<Item> 
     { 
     { new Item { Id = Guid.NewGuid(), Name = "Item 1", Value = 10 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 2", Value = 20 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 3", Value = 30 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 4", Value = 40 } } 
     } 
    }); 
} 

Edición # 2

HttpPost Acción

[HttpPost] 
public ActionResult Test(ItemWrapper w) 
{ 
    if(w.Items == null) 
     Response.Write("Items was null"); 
    else 
     Response.Write("Items found " + w.Items.Count.ToString()); 
    return null; 
} 

Index.aspx

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 

<h4>Does Not Work</h4> 
<% using(Html.BeginForm("Test", "Home")) 
{%> 
     <%:Html.EditorFor(m => m.Items) %> 
     <input type="submit" value-"Go" /> 
<%}%> 

<h4>Does Work</h4> 
     <% using(Html.BeginForm("Test", "Home")) 
     {%> 
    <table> 
     <tbody> 
      <% for(int i = 0; i < Model.Items.Count; i++) 
      {%> 
      <tr> 
       <td><%: Model.Items[i].Name%></td> 
       <td> 
        <%: Html.HiddenFor(m => Model.Items[i].Id) %> 
        <%: Html.TextBoxFor(m => Model.Items[i].Value) %> 
       </td> 
      </tr> 
      <%}%> 
     </tbody> 
    </table> 
      <input type="submit" value-"Go" /> 
     <%}%> 

</asp:Content> 

Respuesta

7

he entendido su problema, y ​​podría muy bien tener una solución demasiado :)!

Primero, déjame explicarte lo que he aprendido inspeccionando el framework's source code (siempre es una buena idea inspeccionar el código fuente de un proyecto de código abierto para comprender mejor cómo funcionan ciertas cosas).

1-) Al utilizar sencilla establecimiento inflexible ayudantes HTML (es decir, todos Html.xxxFor (...) métodos, excepto EditorFor y DisplayFor), en la expresión lambda que definen la propiedad del modelo para hacer, el nombre del elemento hTML generado es igual a cualquier cadena siguiente "modelo =>", menos lo que viene antes de "=>", es decir:

  • la cadena "modelo" si el modelo es una colección
  • o la cadena "modelo. "(tenga en cuenta" . "al final) de lo contrario.

Así, por ejemplo, esto:

<%: Html.TextBoxFor(m=>m.OneProperty.OneNestedProperty)%> 

generará esta salida html:

<input type="text" name="OneProperty.OneNestedProperty" ../> 

Y esto:

<%: Html.TextBoxFor(m=>m[0].OneProperty.OneNestedProperty)%> 

generará esto:

<input type="text" name="[0].OneProperty.OneNestedProperty" ../> 

==> Esto explica en parte por qué tiene esta salida html "extraña" cuando usa EditorFor.

2-) Al utilizar complejos inflexible ayudantes (EditorFor y DisplayFor), la misma regla anterior se aplica dentro de la vista parcial asociado (ItemList.ascx en su caso), y en Además, todos los elementos html generados serán con el prefijo por lo que viene después de "==>", como se explica en 1-).

El prefijo es aquí "Artículos", porque tiene esto en su vista con tipo (Index.aspx):

<%:Html.EditorFor(m => m.Items) %> 

==> Esto explica por completo la salida, y por defecto aglomerante ya no funciona con su lista de artículos

La solución será hueco por su ItemWrapper parámetro en el [HttpPost] Método de, en sus propiedades, y luego usar la Attribute Enlazar con su parámetro Prefijo para cada propiedad compleja, como este:

[HttpPost] 
    public string Index(string foo,[Bind(Prefix = "Items.")]IList<Item> items) 
    { 
     return "Hello"; 
    } 

(suponiendo que ItemWrapper también tiene un simple propiedad denominada Foo de tipo cadena)

para evitar conflictos, cuando se enumeran las propiedades en el método post, os recomiendo dar un nombre a sus parámetros de acuerdo con EA El nombre de la propiedad de ch (no importa el caso) como yo.

Espero que esto ayude!

+0

lo que es realmente una peculiaridad en la forma MVC genera los nombres de campo. La vista parcial no tiene en cuenta que el modelo es una colección al generar el nombre del campo. Supongo que si los artículos.se crea en el nivel de vista en lugar del nivel de vista parcial, entonces puede que no haya una buena manera de solucionarlo. Gracias. –

+0

kondotine: suena como un error de asp.net mvc, ¿alguien lo ha informado? – Wout

+0

Ok, informé de mí mismo: http://aspnet.codeplex.com/workitem/7711, ¡vote por esta corrección de errores! – Wout

-1

Una solución más perezosa es simplemente usar jQuery para "arreglar" instancias de este tipo. basta con ejecutar la siguiente función después de la página (o parte de la página) cargas:

function makeHiddenInputBindable() { 
    $('input[type="hidden"]').each(
     function (i) { 
      $(this).attr('name', function() { 
       return this.name.replace(/\.\[/g, "["); 
      }) 
     } 
    ); 
} 
+1

jQuery no se debe usar como una muleta para volver a trabajar HTML roto. Solucionarlo en el lado del servidor. –

Cuestiones relacionadas