プロパティとしてリストを含むモデルを使用しています。このリストには、SQL Serverから取得したアイテムを入力しています。リストをビューで非表示にして、POSTアクションに渡すようにします。後で、jQueryを使用してこのリストにさらに項目を追加すると、配列を後で拡張するのに適さないようにすることができます。通常は使用します
@Html.HiddenFor(model => model.MyList)
この機能を実現しますが、何らかの理由でPOSTのリストは常にnullです。
非常に簡単な質問です。MVCがこのように動作する理由は誰でも知っていますか?
私はこの問題に出くわし、次のことを実行するだけで解決しました。
@for(int i = 0; i < Model.ToGroups.Length; i++)
{
@Html.HiddenFor(model => Model.ToGroups[i])
}
Foreachの代わりにforを使用すると、モデルバインディングが正しく機能し、リスト内のすべての非表示の値が取得されます。この問題を解決する最も簡単な方法のようです。
HiddenForはDisplayForやEditorForとは異なります。コレクションでは機能せず、単一の値のみが機能します。
MVC Futuresプロジェクトで使用可能なSerialize HTMLヘルパーを使用して、オブジェクトをHiddenフィールドにシリアル化できます。または、コードを自分で記述する必要があります。より良い解決策は、何らかのIDを単純にシリアル化し、ポストバック時にデータベースからデータを再取得することです。
ちょっとしたハックですが、リストで@Html.EditorFor
または@Html.DisplayFor
が機能する場合、投稿リクエストで送信されるが表示されないことを確認したい場合は、display: none;
代わりに非表示にします。例:
<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
Newtonsoftを使用してオブジェクトをJSON文字列に逆シリアル化し、それをHiddenフィールドに挿入する方法はどうですか。 (Model.DataResponse.Entity.Commissionは、単純な"CommissionRange"オブジェクトのListです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]
}
レンダリング:
<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}]">
私の場合、ポストバックする前に、非表示フィールドのjsonを編集するためにいくつかのJSを行います
私のコントローラーでは、再びNewtonsoftを使用して逆シリアル化します。
string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
Html.HiddenFor
は、1つの値のみのために設計されています。非表示フィールドを作成する前に、何らかの方法でリストをシリアル化する必要があります。
たとえば、リストのタイプが文字列の場合、リストをカンマ区切りリストに結合し、コントローラーにポストバックした後にリストを分割できます。
非表示になっているのはEditorForに従う必要があることがわかりました(モデル値がコントローラーに戻らない理由を解明しようとして数時間後)。
私が他の何か間違ったことをしていない限り、これは私が見つけたものです。私は二度と間違いを犯しません。
別のクラスのリストを含むモデルのコンテキスト内。
これは機能しません:
@{
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>
}
}
このように......
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>
}
これを確認できます solution 。
EditorTemplate内にHiddenForのみを配置します。
そして、あなたのビューにこれを入れてください:@Html.EditorFor(model => model.MyList)
動作するはずです。
私はHiddenFor
のソースコードを掘り始めました。あなたが見ている障害は、あなたの複雑なオブジェクトMyList
がstring
型に暗黙的に変換できないため、フレームワークがModel
の値はnull
であり、value
属性を空にします。
同じ問題に直面した。 forループなしでは、リストの最初の要素のみがポストされます。 forループを繰り返し処理した後、完全なリストを保持して正常に投稿できます。
@if (Model.MyList!= null)
{
for (int i = 0; i < Model.MyList.Count; i++)
{
@Html.HiddenFor(x => x.MyList[i])
}
}
別のオプションは次のとおりです。
<input type="hidden" value=@(string.Join(",", Model.MyList)) />
これを修正する別の可能な方法は、リスト内の各オブジェクトにIDを与え、@Html.DropDownListFor(model => model.IDs)
を使用して、IDを保持する配列を設定することです。
多分遅くなりますが、コレクションの隠しフィールドの拡張メソッドを作成しました(単純なデータ型のアイテムを使用):
だからここにある:
/// <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());
}
使い方は次のように簡単です。
@Html.HiddenForCollection(m => m.MyList)
foreach
ループの代わりにfor
ループを使用すると、わずかにクリーンなソリューションになります。
@foreach(var item in Model.ToGroups)
{
@Html.HiddenFor(model => item)
}