@ Html.HiddenFor tidak bekerja pada Daftar di ASP.NET MVC

97

Saya menggunakan model yang berisi List sebagai properti. Saya mengisi daftar ini dengan item yang saya ambil dari SQL Server. Saya ingin List disembunyikan dalam tampilan dan diteruskan ke tindakan POST. Nanti saya mungkin ingin menambahkan lebih banyak item ke Daftar ini dengan jQuery yang membuat array tidak cocok untuk ekspansi nanti. Biasanya Anda akan menggunakan

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

untuk mencapai fungsi ini, tetapi untuk beberapa alasan List di POST selalu null.

Pertanyaan yang sangat sederhana, ada yang tahu kenapa MVC berperilaku seperti ini?

Anton Smith
sumber
1
Biasanya Anda tidak akan menyembunyikan seluruh daftar seperti itu. Apa output yang Anda inginkan dalam hal <input />s?
Cᴏʀʏ
1
apa MyListisinya? HiddenForhanya digunakan untuk satu masukan dalam satu waktu.
Daniel A. White
1
Jenis apa itu Model.MyList? Anda mungkin perlu melakukan beberapa serialisasi / deserialisasi pada daftar Anda secara manual.
Kyle Trauberman
1
[ stackoverflow.com/questions/4381871/… Pertanyaan serupa.
Sanjeevi Subramani
1
Pertanyaan Serupa: Penggunaan HiddenFor dengan intellisense
Sanjeevi Subramani

Jawaban:

161

Saya baru saja menemukan masalah ini dan menyelesaikannya hanya dengan melakukan hal berikut:

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

Dengan menggunakan for dan bukan foreach, pengikatan model akan bekerja dengan benar dan mengambil semua nilai tersembunyi Anda dalam daftar. Sepertinya cara termudah untuk mengatasi masalah ini.

Daniel Mackay
sumber
5
Terima kasih! menyelamatkan malam saya.
TSmith
7
Terima kasih - solusi sederhana yang bagus. Hanya diperlukan mod kecil: Bidang Id objek perlu dirujuk. Jadi jika bidangnya disebut RowId, maka:@Html.HiddenFor(model => Model.ToGroups[i].RowId)
Krishna Gupta
3
bekerja untuk saya, bahkan ketika saya memiliki beberapa bidang pada model dalam koleksi. Yaitu @Html.EditorFor(model => Model.ToGroups[i].Id)diikuti oleh @Html.EditorFor(model => Model.ToGroups[i].Description)di lain waktu - keduanya di loop-for. Dan pengontrol dapat memetakannya ke daftar model dengan bidang tersebut. Dan untuk memastikan tidak ada yang muncul di layar, cukup lilitkan di<div style="display: none;"></div>
Don Cheadle
Cemerlang! Selesai dengan Baik. Bekerja untuk saya!
AxleWack
3
@ user3186023 Menjawab komentar yang sangat lama di sini, tapi mungkin orang lain akan memiliki masalah yang sama: Ubah for-loop menjadi ini:for(int i = 0; i < Model.Departments.Count(); i++)
Stian
28

HiddenFor tidak seperti DisplayFor atau EditorFor. Itu tidak akan bekerja dengan koleksi, hanya nilai tunggal.

Anda dapat menggunakan helper Serialize HTML yang tersedia di proyek MVC Futures untuk membuat serial objek ke bidang Tersembunyi, atau Anda harus menulis kodenya sendiri. Solusi yang lebih baik adalah dengan membuat serialisasi ID dan mendapatkan kembali data dari database di postback.

Erik Funkenbusch
sumber
Apakah kamu punya contoh? Saya mencoba ini dan gagal mengikat ke nilai ViewModel saat formulir dikirim.
Alan Macdonald
@AlanMacdonald - jika ada sesuatu yang gagal diikat, itu karena penamaan Anda salah, kemungkinan besar karena Anda menggunakan foreach dan bukan for dengan pengindeks. Atau mungkin Anda tidak menggunakan atribut yang tepat dalam pengikatan. Lihat weblogs.asp.net/shijuvarghese/archive/2010/03/06/…
Erik Funkenbusch
Terima kasih. Sebenarnya ketika saya mencobanya secara harfiah adalah @ Html.Serialize ("Model.ModelIDs", Model.ModelIDs) di mana Model adalah ViewModel saya dan memiliki properti array int ModelIDs. Jadi tidak ada loop atau apapun. Saat formulir dikirimkan, ModelID selalu nol di ViewModel terikat.
Alan Macdonald
@AlanMacdonald - Anda tidak menyertakan "Model" di nama.
Erik Funkenbusch
16

Ini sedikit meretas, tetapi jika @Html.EditorForatau @Html.DisplayForberfungsi untuk daftar Anda, jika Anda ingin memastikannya dikirim pada permintaan posting tetapi tidak terlihat, Anda bisa menatanya menggunakan display: none;untuk menyembunyikannya, misalnya:

<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
Mark Rhodes
sumber
Ini tidak menyimpan nilai dalam model pada posting permintaan.
nldev
Jika .EditorFor diatur untuk bekerja dengan benar, maka ini harus bekerja juga, saya yakin.
Mark Rhodes
9

Bagaimana dengan menggunakan Newtonsoft untuk deserialisasi objek menjadi string json dan kemudian memasukkannya ke dalam bidang Tersembunyi Anda misalnya ( Model.DataResponse.Entity.Commission adalah Daftar objek "CommissionRange" sederhana seperti yang akan Anda lihat di 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]
   }

Ditampilkan sebagai:

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

Dalam kasus saya, saya melakukan beberapa hal JS untuk mengedit json di bidang tersembunyi sebelum memposting kembali

Di pengontrol saya, saya kemudian menggunakan Newtonsoft lagi untuk deserialize:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
Adam Hei
sumber
Ini berhasil untuk saya. Saya pikir itu jauh lebih bersih daripada solusi yang diterima.
e-pada
6

Html.HiddenFordirancang hanya untuk satu nilai. Anda perlu menyusun daftar Anda dengan cara tertentu sebelum membuat bidang tersembunyi.

Misalnya, jika daftar Anda berjenis string, Anda dapat menggabungkan daftar tersebut menjadi daftar yang dipisahkan koma, kemudian memisahkan daftar setelah posting kembali di pengontrol Anda.

Kyle Trauberman
sumber
4

Saya baru saja menemukan (setelah beberapa jam mencoba mencari tahu mengapa nilai model tidak kembali ke pengontrol) yang disembunyikan untuk harus mengikuti EditorFor.

Kecuali saya melakukan kesalahan lain, inilah yang saya temukan. Saya tidak akan membuat kesalahan lagi.

Dalam konteks Model yang berisi daftar kelas lain.

Ini TIDAK akan berhasil:

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

Dimana ini AKAN ......

            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>
            }
AntDC
sumber
3

Saya mulai menggali kode sumber untuk HiddenFor, dan saya pikir penghalang yang Anda lihat adalah bahwa objek kompleks Anda MyListtidak secara implisit dapat diubah menjadi tipe string, jadi kerangka kerja memperlakukan Modelnilai Anda sebagai nulldan membuat valueatribut kosong.

Cᴏʀʏ
sumber
3

Anda dapat melihat solusi ini .

Letakkan hanya HiddenFor di dalam EditorTemplate.

Dan dalam Pandangan Anda, tulis ini: @Html.EditorFor(model => model.MyList)

Ini harus berhasil.

Wilfart Benjamin
sumber
3

Menghadapi masalah yang sama. Tanpa for loop, itu hanya memposting elemen pertama dari daftar. Setelah melakukan iterasi melalui for loop, itu dapat menyimpan daftar lengkap dan memposting dengan sukses.

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

Pilihan lainnya adalah:

<input type="hidden" value=@(string.Join(",", Model.MyList)) />
TiagoBrenck
sumber
Ini juga ide pertamaku. Tapi saya punya model tampilan, yang diharapkan int [] untuk bidang MyList dan string yang dipisahkan koma tidak diurai menjadi array dengan mekanisme pengikatan MVC.
Tadej Mali
2

The foreachLoop bukannya forlingkaran mungkin menjadi solusi sedikit lebih bersih.

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

Cara lain yang mungkin untuk memperbaikinya adalah dengan memberi setiap objek dalam Daftar Anda sebuah ID, lalu menggunakan @Html.DropDownListFor(model => model.IDs)dan mengisi larik yang menyimpan ID tersebut.

deckeresq
sumber
1

mungkin terlambat, tetapi saya membuat metode ekstensi untuk bidang tersembunyi dari koleksi (dengan item tipe data sederhana):

Jadi begini:

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

Penggunaannya sesederhana:

@Html.HiddenForCollection(m => m.MyList)
Gh61
sumber