2012-07-26 33 views
18

Disculpas si esto se ha hecho antes; hay un millón de formas de expresarlo, por lo que buscar una respuesta ha resultado difícil.MVC3 - viewmodel con la lista de tipos complejos

que tienen un modelo de vista con las siguientes propiedades:

public class AssignSoftwareLicenseViewModel 
{ 
    public int LicenseId { get; set; } 
    public ICollection<SelectableDeviceViewModel> Devices { get; set; } 
} 

Una versión simplificada de SelectableDeviceViewModel sería la siguiente:

public class SelectableDeviceViewModel 
{ 
    public int DeviceInstanceId { get; set; } 
    public bool IsSelected { get; set; } 
    public string Name { get; set; } 
} 

en mi opinión, que estoy tratando de mostrar una lista de casillas editables para la propiedad Dispositivos, dentro de un formulario de entrada. Actualmente, mi vista se ve así:

@using (Html.BeginForm()) 
{ 
    @Html.HiddenFor(x => Model.LicenseId) 
    <table> 
     <tr> 
      <th>Name</th> 
      <th></th> 
     </tr> 
     @foreach (SelectableDeviceViewModel device in Model.Devices) 
     { 
      @Html.HiddenFor(x => device.DeviceInstanceId) 
      <tr> 
       <td>@Html.CheckBoxFor(x => device.IsSelected)</td> 
       <td>@device.Name</td> 
      </tr> 
     } 
    </table> 

    <input type="submit" value="Assign" /> 
} 

El problema es que cuando el modelo que se envía de vuelta al controlador, dispositivos es nula.

Mi suposición es que esto está sucediendo, porque a pesar de que estoy editando su contenido, la propiedad dispositivos no se incluye explícitamente en el formulario. Intenté incluirlo con HiddenFor, pero eso solo dio como resultado que el modelo tuviera una lista vacía en lugar de nula.

Alguna idea de lo que estoy haciendo mal aquí?

+0

¿Puede mostrarnos el código de su controlador que está rellenando el modelo de datos? – nikeaa

+2

+1 para la pregunta más elegantemente descrita que cubre esta área. Créanme, ¡hemos pasado las últimas 2 horas buscando este problema! –

Respuesta

30

Mi suposición es que esto está sucediendo porque a pesar de que soy edición de su contenido, la propiedad dispositivos no explícitamente está incluido en el formulario.

No, su suposición es incorrecta. La razón por la que esto no se enlaza correctamente es porque sus campos de entrada no tienen nombres correctos. Por ejemplo se les llama name="IsSelected" en lugar de name="Devices[0].IsSelected". Eche un vistazo al formato de cable correcto que debe usarse para enlazar a las colecciones: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

¿Pero por qué sucede esto?

Ocurre debido al foreach bucle que utilizó en su vista. Que utilizó x => device.IsSelected como expresión lambda para la casilla de verificación, pero esto no tiene en cuenta la propiedad de dispositivos en absoluto (como se puede ver mirando el código fuente generado de su página web).

Entonces, ¿qué debería hacer?

Personalmente, le recomendaría utilizar plantillas de editor ya que respetan el contexto de navegación de propiedades complejas y generan nombres de entrada correctos. Así que deshacerse de todo el foreach bucle en su opinión y reemplazarlo con una sola línea de código:

@Html.EditorFor(x => x.Devices) 

y ahora definir una plantilla editor personalizado que automáticamente se dictó por ASP.NET MVC para cada elemento de la Colección de dispositivos.Advertencia: la ubicación y el nombre de esta plantilla son muy importantes ya que esto funciona mediante la convención: ~/Views/Shared/EditorTemplates/SelectableDeviceViewModel.cshtml:

@model SelectableDeviceViewModel 
@Html.HiddenFor(x => x.DeviceInstanceId) 
<tr> 
    <td>@Html.CheckBoxFor(x => x.IsSelected)</td> 
    <td>@Html.DisplayFor(x => x.Name)</td> 
</tr> 

Otro enfoque (que no recomiendo) es cambiar su actual ICollection en su modelo de vista a un indexadas colección (tal como un IList<T> o una matriz T[]):

public class AssignSoftwareLicenseViewModel 
{ 
    public int LicenseId { get; set; } 
    public IList<SelectableDeviceViewModel> Devices { get; set; } 
} 

y entonces en lugar de la foreach utilizar un bucle for :

@for (var i = 0; i < Model.Devices.Count; i++) 
{ 
    @Html.HiddenFor(x => x.Devices[i].DeviceInstanceId) 
    <tr> 
     <td>@Html.CheckBoxFor(x => x.Devices[i].IsSelected)</td> 
     <td>@Html.DisplayFor(x => x.Devices[i].Name</td> 
    </tr> 
} 
+1

La plantilla del editor funciona perfectamente; ¡muchas gracias! – InsqThew

+1

Excelente respuesta, pero ¿por qué no recomienda usar colecciones indexadas en el modelo de vista? –

+1

@JamieIde, porque prefiero usar plantillas de editor. ¿Por qué escribir bucles en las vistas cuando el marco ya lo ha tenido en cuenta? –

0

EditorLas plantillas funcionan y mantienen el código limpio. No necesita los bucles y el modelo se envió correctamente.

Sin embargo, ¿alguien ha tenido problemas con la validación en viewmodels complejos (plantillas anidadas de EditorFor)? Estoy usando Kendo Validator y estoy corriendo en todo tipo de errores jquery.

Cuestiones relacionadas