@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?
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.
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.
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>
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);
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.
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>
}
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.
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.
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])
}
}
une autre option serait:
<input type="hidden" value=@(string.Join(",", Model.MyList)) />
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.
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)