@ Html.HiddenForがASP.NET MVCのリストで機能しない


97

プロパティとしてリストを含むモデルを使用しています。このリストにSQL Serverから取得したアイテムを入力しています。リストをビューで非表示にして、POSTアクションに渡します。後で、jQueryを使用してこのリストにさらに項目を追加したい場合があります。これにより、配列が後で拡張するのに適さなくなります。通常は使用します

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

この機能を実現するためですが、何らかの理由でPOSTのリストは常にnullです。

非常に単純な質問ですが、MVCがこのように動作する理由を誰かが知っていますか?


1
通常、そのようなリスト全体を非表示にすることはありません。<input />s に関して望ましい出力は何ですか?
Cᴏʀʏ

1
何がMyList含まれていますか?HiddenFor一度に1つの入力にのみ使用されます。
ダニエルA.ホワイト

1
タイプはModel.MyList何ですか?リストのシリアル化/逆シリアル化を手動で実行する必要がある場合があります。
カイルトラウバーマン


回答:


161

私はこの問題に遭遇し、次のことを行うだけで解決しました:

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

foreachの代わりにforを使用すると、モデルバインディングが正しく機能し、リスト内のすべての非表示の値を取得します。この問題を解決する最も簡単な方法のようです。


5
ありがとう!私の夜を救った。
TSmith、

7
ありがとう-素敵なシンプルなソリューション。ただし、ほんの小さなmodが必要です。オブジェクトのIdフィールドを参照する必要があります。その後、フィールドが呼び出された場合のRowId:だから、@Html.HiddenFor(model => Model.ToGroups[i].RowId)
クリシュナグプタ

3
コレクションのモデルに複数のフィールドがある場合でも、私のために働きました。つまりは@Html.EditorFor(model => Model.ToGroups[i].Id)続く@Html.EditorFor(model => Model.ToGroups[i].Description)forループの両方で-次回に。そしてコントローラーはそれをそれらのフィールドを持つモデルのリストにマッピングすることができました。画面に表示されないようにするには、囲んでください<div style="display: none;"></div>
Don Cheadle

鮮やかさ!よくできました。私のために働いた!
AxleWack 2017年

3
@ user3186023ここで本当に古いコメントに答えますが、おそらく他の誰かが同じ問題を抱えています:for-loopをこれに変更します:for(int i = 0; i < Model.Departments.Count(); i++)
Stian

28

HiddenForはDisplayForやEditorForとは異なります。コレクションでは機能せず、単一の値でのみ機能します。

MVC Futuresプロジェクトで利用可能なSerialize HTMLヘルパーを使用して、オブジェクトを非表示フィールドにシリアル化するか、自分でコードを記述する必要があります。より良い解決策は、ある種のIDを単純にシリアル化し、ポストバック時にデータベースからデータを再取得することです。


例はありますか?これを試しましたが、フォームが送信されたときにViewModel値にバインドできませんでした。
アランマクドナルド

@AlanMacdonald-何かがバインドに失敗した場合、それはおそらくあなたの命名が正しくないためです。おそらく、with withインデクサーの代わりにforeachを使用したためです。または、バインディングで適切な属性を使用しなかった可能性があります。weblogs.asp.net/shijuvarghese/archive/2010/03/06/…を
Erik Funkenbusch 2013

ありがとう。実際に試したところ、文字通り@ Html.Serialize( "Model.ModelIDs"、Model.ModelIDs)でした。Modelは私のViewModelで、ModelIDs int配列プロパティがありました。だからループも何もありませんでした。フォームが送信されたとき、バインドされたViewModelのModelIDは常にnullでした。
アランマクドナルド

@AlanMacdonald-名前に「モデル」を含めません。
エリックファンケンブッシュ2013

16

これは、ハッキングのビットだが、場合@Html.EditorFor@Html.DisplayFor、あなたが確認する場合は、作業リストのために、それはあなたが使用して、それを単にスタイルを可能性がPOSTリクエストで送信されますが表示されていないdisplay: none;、代わりに、例えばそれを非表示にします:

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

これは、リクエストの投稿時にモデルに値を保存しません。
nldev 2016年

.EditorForが正しく機能するように設定されている場合、これも機能するはずです。
マークロードス

9

Newtonsoftを使用してオブジェクトをjson文字列に逆シリアル化し、それを非表示フィールドに挿入するのはどうですか(例:Model.DataResponse.Entity.Commissionは、JSONに表示される単純な"CommissionRange"オブジェクトのリストです)

@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);

これでうまくいきました。私はそれが受け入れられた解決策よりもずっときれいだと思った。
e-on

6

Html.HiddenForは1つの値のみを対象に設計されています。非表示フィールドを作成する前に、何らかの方法でリストをシリアル化する必要があります。

たとえば、リストのタイプが文字列の場合、リストをカンマ区切りのリストに結合し、コントローラにポストバックした後にリストを分割できます。


4

私は(モデル値がコントローラーに戻されなかった理由を理解しようとする数時間後に)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>
            }

3

のソースコードの調査を開始しましたHiddenForが、複雑なオブジェクトMyListは暗黙的にtype stringに変換できないため、フレームワークはModel値をとして扱いnullvalue属性を空にレンダリングしていると思います。



3

同じ問題に直面しました。forループがなければ、リストの最初の要素のみをポストしました。forループを繰り返した後、完全なリストを保持し、正常に投稿できます。

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

2

別のオプションは次のとおりです。

<input type="hidden" value=@(string.Join(",", Model.MyList)) />

これも私の最初のアイデアでした。しかし、私はビューモデルを持っていました。MyListフィールドのint []を期待し、コンマ区切りの文字列はMVCバインディングメカニズムによって配列に解析されませんでした。
Tadej Mali

2

foreach代わりのループforループは少しクリーナー解決策になるかもしれません。

@foreach(var item in Model.ToGroups)
{
    @Html.HiddenFor(model => item)
}

1

これを修正するもう1つの方法は、リスト内の各オブジェクトに@Html.DropDownListFor(model => model.IDs)IDを指定し、IDを保持する配列を使用してデータを設定することです。


1

多分遅いですが、コレクションから非表示フィールドの拡張メソッドを作成しました(単純なデータ型の項目を使用):

だからここにあります:

/// <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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.