2012-02-21 34 views
63

Estoy usando un modelo que contiene una lista como propiedad. Estoy poblando esta lista con elementos que tomo de SQL Server. Quiero que la Lista se oculte en la vista y pase a la acción POST. Más adelante, es posible que desee agregar más elementos a esta lista con jQuery, lo que hace que una matriz no sea adecuada para la expansión más adelante. Normalmente usaría@ Html.HiddenFor no funciona en listas en ASP.NET MVC

@Html.HiddenFor(model => model.MyList) 

para lograr esta funcionalidad, pero por algún motivo la Lista en POST siempre es nula.

Pregunta muy simple, ¿alguien sabe por qué MVC se comporta así?

+0

Normalmente, no se ocultan listas completas como esa. ¿Cuál es su salida deseada en términos de '' s? –

+0

¿qué contiene 'MyList'? 'HiddenFor' solo se usa para una entrada a la vez. –

+0

¿Qué tipo es 'Model.MyList'? Es posible que deba realizar una serialización/deserialización en su lista de forma manual. –

Respuesta

91

acabo de venir a través de este tema y lo resolvió simplemente haciendo lo siguiente:

for(int i = 0; i < Model.ToGroups.Count; i++) 
{ 
    @Html.HiddenFor(model => Model.ToGroups[i]) 
} 

Mediante el uso de un pues en lugar de un foreach el modelo de unión funcionará correctamente y recoger todos sus valores ocultos en la lista. Parece la forma más simple de resolver este problema.

+0

Gracias! me salvó la noche – TSmith

+5

Gracias - buena solución simple. No obstante, solo se necesita una pequeña modificación: el campo Id del objeto necesita referencia. Entonces, si el campo se llama RowId, entonces: '@ Html.HiddenFor (model => Model.ToGroups [i] .RowId)' –

+2

funcionó para mí, incluso cuando tenía varios campos en los modelos de la colección. Es decir. '@ Html.EditorFor (model => Model.ToGroups [i] .Id)' seguido de '@ Html.EditorFor (model => Model.ToGroups [i] .Description)' la próxima vez, ambos en el for- lazo. Y el controlador fue capaz de asignarlo a una lista de los modelos con esos campos. Y para asegurarse de que nada de eso apareció en la pantalla, simplemente rodéelo en '

' – mmcrae

5

Html.HiddenFor está diseñado para un solo valor. Tendrá que serializar su lista de alguna manera antes de crear el campo oculto.

Por ejemplo, si su lista es de tipo cadena, puede unir la lista a una lista separada por comas, luego dividir la lista después de la publicación en su controlador.

27

HiddenFor no es como un DisplayFor o un EditorFor. No funcionará con colecciones, solo valores únicos.

Puede usar el ayudante Serialize HTML disponible en el proyecto MVC Futures para serializar un objeto a un campo Oculto, o tendrá que escribir el código usted mismo. Una mejor solución es simplemente serializar una identificación de algún tipo y volver a obtener los datos de la base de datos en la devolución de datos.

+0

¿Tiene un ejemplo? ? Intenté esto y no se pudo vincular al valor de ViewModel cuando se envió el formulario. –

+0

@AlanMacdonald: si algo no se puede vincular, es porque su nombre no es correcto, más que probable porque usó un foreach en lugar de un for con el indexador. O tal vez no usaste los atributos adecuados en el enlace. Ver http://weblogs.asp.net/shijuvarghese/archive/2010/03/06/persisting-model-state-in-asp-net-mvc-using-html-serialize.aspx –

+0

Gracias. En realidad, cuando lo probé fue literalmente @ Html.Serialize ("Model.ModelIDs", Model.ModelIDs) donde Model era mi ViewModel y tenía una propiedad int array ModelIDs. Entonces no hubo bucles ni nada. Cuando se envió el formulario, los ID de modelo siempre fueron nulos en el ViewModel vinculado. –

1

empecé a cavar a través del código fuente para HiddenFor, y creo que el control de carretera que se está viendo es que su objeto complejo MyList no es convertir implícitamente al tipo string, por lo que el marco trata a su valor Model como null y hace que la value atributo vacío.

2

Puedes echar un vistazo a este solution.

Ponga solo HiddenFor dentro de la plantilla del editor.

Y en su opinión a poner esto: @Html.EditorFor(model => model.MyList)

Debería obras.

12

Es un poco de un truco, pero si @Html.EditorFor o @Html.DisplayFor trabajo para su lista, si usted quiere asegurarse de que sea enviado en la solicitud de puesto, pero no visible, que sólo podría diseñarlo a la utilización de display: none; de ocultarlo en su lugar, por ejemplo:

<div style="display: none;">@Html.EditorFor(model => model.MyList)</div> 
+0

Esto no guarda el valor en el modelo en la publicación de la solicitud. – nldev

+0

Si .EditorFor está configurado para funcionar correctamente, entonces esto debería funcionar también, creo. –

0

Otra posible manera de solucionar este problema sería la de dar a cada objeto en la lista de un documento de identidad, a continuación, utilizar @Html.DropDownListFor(model => model.IDs) y rellenar una matriz que contiene los identificadores.

3

Me acabo de enterar (después de un par de horas de tratar de averiguar por qué los valores del modelo no volvían al controlador) que oculto debe seguir el EditorFor.

A menos que haga algo incorrecto, esto es lo que encontré. No cometeré el error otra vez.

En el contexto de un modelo que contiene una lista de otra clase.

esto no funcionará:

 @{ 
      for (int i = 0; i < Model.Categories.Count; i++) 
      { 
       <tr> 
        <td> 
         @Html.HiddenFor(modelItem => Model.Categories[i].Id) 
         @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId) 
         @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName)        
         @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName)        
        </td> 
        <td> 
         @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit)               
         @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit) 
         @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit) 
        </td> 
        <td style="text-align: center"> 
         @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected)        
         @Html.EditorFor(modelItem => Model.Categories[i].IsSelected) 
        </td> 
       </tr> 
      } 
     } 

Donde ya que esto ......

  for (int i = 0; i < Model.Categories.Count; i++) 
      { 
       <tr> 
        <td> 
         @Html.HiddenFor(modelItem => Model.Categories[i].Id) 
         @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId) 
         @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName)        
         @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName)        
        </td> 
        <td> 
         @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit) 
         @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit)        
         @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit) 
        </td> 
        <td style="text-align: center"> 
         @Html.EditorFor(modelItem => Model.Categories[i].IsSelected) 
         @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected)        
        </td> 
       </tr> 
      } 
6

Qué acerca del uso Newtonsoft deserializar el objeto en una cadena JSON y luego insertar en que su campo oculto, por ejemplo (Model.DataResponse.Entity.Commission es una lista de sencilla "CommissionRange" objetos como se verá en el JSON)

@using (Ajax.BeginForm("Settings", "AffiliateProgram", Model.DataResponse, new AjaxOptions { UpdateTargetId = "result" })) 
    { 
     string commissionJson = JsonConvert.SerializeObject(Model.DataResponse.Entity.Commission); 
     @Html.HiddenFor(data => data.DataResponse.Entity.Guid) 
     @Html.Hidden("DataResponse_Entity_Commission", commissionJson) 
     [Rest of my form] 
    } 

Renders como:

<input id="DataResponse_Entity_Commission" name="DataResponse_Entity_Commission" type="hidden" value="[{"RangeStart":0,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":1,"RangeEnd":2,"CommissionPercent":3.00000},{"RangeStart":2,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":3,"RangeEnd":2,"CommissionPercent":1.00000},{"RangeStart":15,"RangeEnd":10,"CommissionPercent":5.00000}]"> 

En mi caso, hago algunas cosas JS para editar el json en el campo oculto antes de publicar de nuevo

En mi controlador, entonces utilizar Newtonsoft nuevo deserializar:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"]; 
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange); 
+0

Esto funcionó para mí. Pensé que era mucho más limpio que el solución aceptada. –

0

tal vez tarde, pero he creado método de extensión de los campos ocultos de la colección (con elementos de tipos de datos simples):

Así que aquí está:

/// <summary> 
/// Returns an HTML hidden input element for each item in the object's property (collection) that is represented by the specified expression. 
/// </summary> 
public static IHtmlString HiddenForCollection<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) where TProperty : ICollection 
{ 
    var model = html.ViewData.Model; 
    var property = model != null 
       ? expression.Compile().Invoke(model) 
       : default(TProperty); 

    var result = new StringBuilder(); 
    if (property != null && property.Count > 0) 
    { 
     for(int i = 0; i < property.Count; i++) 
     { 
      var modelExp = expression.Parameters.First(); 
      var propertyExp = expression.Body; 
      var itemExp = Expression.ArrayIndex(propertyExp, Expression.Constant(i)); 

      var itemExpression = Expression.Lambda<Func<TModel, object>>(itemExp, modelExp); 

      result.AppendLine(html.HiddenFor(itemExpression).ToString()); 
     } 
    } 

    return new MvcHtmlString(result.ToString()); 
} 

El uso es tan simple como:

@Html.HiddenForCollection(m => m.MyList) 
2

Ha tenido el mismo problema. Sin for loop, solo publicó el primer elemento de la lista. Después de iterar a través del ciclo, puede mantener la lista completa y publicar con éxito.

@if (Model.MyList!= null) 
    { 
    for (int i = 0; i < Model.MyList.Count; i++) 
     { 
     @Html.HiddenFor(x => x.MyList[i]) 
     } 
    } 
Cuestiones relacionadas