@HTML.HiddenFor ne fonctionne pas sur les listes ASP.NET MVC

j'utilise un modèle qui contient une liste comme propriété. J'empile cette liste avec les éléments que J'attrape sur le serveur SQL. Je veux que la liste soit cachée dans la vue et transmise à L'action POST. Plus tard, je voudrais peut-être ajouter d'autres éléments à cette liste avec jQuery qui rend un tableau Impropre à l'expansion plus tard. Normalement vous utiliseriez

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

pour accomplir cette fonctionnalité, mais pour une raison quelconque la liste dans le POST est toujours nulle.

question très simple, quelqu'un sait pourquoi MVC se comporte ainsi?

71
demandé sur Keerthi 2012-02-22 01:15:26

12 réponses

je viens de rencontrer ce problème et l'ai résolu simplement en faisant ce qui suit:

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

en utilisant un for au lieu d'un foreach, la liaison du modèle fonctionnera correctement et récupérera toutes vos valeurs cachées dans la liste. Ça semble être le moyen le plus simple de résoudre ce problème.

110
répondu Daniel Mackay 2014-09-29 06:13:23

HiddenFor n'est pas comme un DisplayFor ou un EditorFor. Ça ne marchera pas avec les collections, seulement des valeurs uniques.

vous pouvez utiliser le Helper Serialize HTML disponible dans le projet MVC Futures pour sérialiser un objet vers un champ caché, ou vous devrez écrire le code vous-même. Une meilleure solution est de simplement sérialiser un ID d'une sorte et de récupérer les données de la base de données sur postback.

27
répondu Erik Funkenbusch 2012-02-21 21:24:57

c'est un peu un piratage, mais si @Html.EditorFor ou @Html.DisplayFor fonctionne pour votre liste, si vous voulez vous assurer qu'il est envoyé sur la demande de poste mais pas visible, vous pouvez juste le style à l'aide de display: none; pour le cacher à la place, E. g:

<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
12
répondu Mark Rhodes 2014-01-15 15:45:05

Qu'en est-il de L'utilisation de Newtonsoft pour desérialiser l'objet dans une chaîne json et ensuite l'insérer dans votre champ caché par exemple ( Model.DataResponse.Entité.Commission est un Liste de simples "CommissionRange" les objets comme vous le verrez dans le 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]
   }

traduit par:

<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}]">

dans mon cas je fais quelques trucs JS pour éditer le json dans le champ caché avant une publication

dans mon contrôleur J'utilise à nouveau Newtonsoft pour désérialiser:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
6
répondu Adam Hey 2015-11-25 22:37:47

Html.HiddenFor est conçu pour une seule valeur. Vous devrez sérialiser votre liste d'une manière ou d'une autre avant de créer le champ caché.

par exemple, si votre liste est de type string, vous pouvez rejoindre la liste dans une liste séparée par des virgules, puis diviser la liste après avoir posté de nouveau dans votre controller.

5
répondu Kyle Trauberman 2012-02-21 21:22:32

je viens de découvrir (après quelques heures à essayer de comprendre pourquoi les valeurs du modèle ne retournaient pas vers le contrôleur) que hidden for devrait suivre L'EditorFor.

si je fais quelque chose de mal c'est ce que j'ai trouvé. Je ne vais pas faire l'erreur de nouveau.

Dans le cadre d'un Modèle qui contient une liste d'une autre classe.

cela ne marchera pas:

        @{
            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>
            }
        }

où cette VOLONTÉ......

            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>
            }
3
répondu AntDC 2015-10-09 13:30:40

j'ai commencé à creuser le code source de HiddenFor , et je pense que le blocage que vous voyez est que votre objet complexe MyList n'est pas implicitement convertible en type string , donc le cadre traite votre valeur Model comme null et rend l'attribut value vide.

2
répondu Cᴏʀʏ 2012-02-21 21:52:22

vous pouvez jeter un oeil sur cette solution .

mis seulement HiddenFor à l'intérieur de L'Editorteemplate.

et à votre avis mettre ceci: @Html.EditorFor(model => model.MyList)

ça devrait marcher.

2
répondu Wilfart Benjamin 2017-05-23 10:31:15

fait face à la même question. Sans boucle for, il n'posté le premier élément de la liste. Après itération à travers pour boucle, il peut garder la liste complète et afficher avec succès.

 @if (Model.MyList!= null)
    {
    for (int i = 0; i < Model.MyList.Count; i++)
      {
        @Html.HiddenFor(x => x.MyList[i])
      }
    }
2
répondu Keerthi 2017-11-23 09:29:12

une autre option serait:

<input type="hidden" value=@(string.Join(",", Model.MyList)) />
1
répondu TiagoBrenck 2018-03-16 19:30:14

une autre façon possible de corriger cela serait de donner à chaque objet de votre liste un ID, puis d'utiliser @Html.DropDownListFor(model => model.IDs) et de peupler un tableau qui contient les ID.

0
répondu deckeresq 2014-06-24 20:43:24

peut-être tard, mais j'ai créé la méthode d'extension pour les champs cachés de la collecte (avec des éléments de type de données simples):

alors voilà:

/// <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());
}

L'Usage est aussi simple que:

@Html.HiddenForCollection(m => m.MyList)
0
répondu Gh61 2017-10-03 14:30:55