@ Html.HiddenFor không hoạt động trên Danh sách trong ASP.NET MVC


97

Tôi đang sử dụng mô hình có chứa Danh sách dưới dạng thuộc tính. Tôi đang điền danh sách này với các mục tôi lấy từ SQL Server. Tôi muốn Danh sách được ẩn trong chế độ xem và chuyển cho hành động ĐĂNG. Sau này, tôi có thể muốn thêm nhiều mục hơn vào Danh sách này bằng jQuery, điều này làm cho một mảng không thích hợp để mở rộng sau này. Thông thường bạn sẽ sử dụng

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

để thực hiện chức năng này, nhưng vì lý do nào đó, Danh sách trong POST luôn trống.

Câu hỏi rất đơn giản, có ai biết tại sao MVC lại cư xử như vậy không?


1
Thông thường bạn sẽ không ẩn toàn bộ danh sách như vậy. Sản lượng mong muốn của bạn về mặt <input />s là gì?
Cᴏʀʏ

1
cái gì MyListchứa? HiddenForchỉ được sử dụng cho một đầu vào tại một thời điểm.
Daniel A. White

1
Loại là Model.MyListgì? Bạn có thể cần thực hiện một số tuần tự hóa / giải mã hóa trên danh sách của mình theo cách thủ công.
Kyle Trauberman


Câu trả lời:


161

Tôi vừa gặp vấn đề này và giải quyết nó đơn giản bằng cách làm như sau:

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

Bằng cách sử dụng for thay vì foreach, liên kết mô hình sẽ hoạt động chính xác và chọn tất cả các giá trị ẩn của bạn trong danh sách. Có vẻ như đây là cách đơn giản nhất để giải quyết vấn đề này.


5
Cảm ơn! đã cứu đêm của tôi.
TSmith

7
Cảm ơn - giải pháp đơn giản tốt đẹp. Chỉ cần một mod nhỏ cần thiết: Trường Id của đối tượng cần được tham chiếu. Vì vậy, nếu trường được gọi là RowId, thì:@Html.HiddenFor(model => Model.ToGroups[i].RowId)
Krishna Gupta

3
đã làm việc cho tôi, ngay cả khi tôi có nhiều trường trên các mô hình trong bộ sưu tập. Tức @Html.EditorFor(model => Model.ToGroups[i].Id)là tiếp theo @Html.EditorFor(model => Model.ToGroups[i].Description)vào lần tiếp theo - cả hai trong vòng lặp. Và bộ điều khiển có thể ánh xạ nó tới một danh sách các mô hình với các trường đó. Và để chắc chắn rằng không ai trong số đó xuất hiện trên màn hình, chỉ cần bao quanh nó trong<div style="display: none;"></div>
Don Cheadle

Xuất sắc! Hoàn thành tốt. Đã làm cho tôi!
AxleWack

3
@ user3186023 Trả lời một nhận xét thực sự cũ ở đây, nhưng có thể ai đó khác sẽ gặp vấn đề tương tự: Thay đổi for-loop thành thế này:for(int i = 0; i < Model.Departments.Count(); i++)
Stian

28

HiddenFor không giống như DisplayFor hoặc EditorFor. Nó sẽ không hoạt động với các bộ sưu tập, chỉ các giá trị đơn lẻ.

Bạn có thể sử dụng trình trợ giúp Serialize HTML có sẵn trong dự án MVC Futures để tuần tự hóa một đối tượng vào trường Hidden hoặc bạn sẽ phải tự viết mã. Một giải pháp tốt hơn là chỉ cần tuần tự hóa một ID nào đó và lấy lại dữ liệu từ cơ sở dữ liệu khi gửi lại.


Bạn có một ví dụ? Tôi đã thử điều này và nó không thể liên kết với giá trị ViewModel khi biểu mẫu được gửi.
Alan Macdonald

@AlanMacdonald - nếu thứ gì đó không liên kết được, đó là do cách đặt tên của bạn không chính xác, nhiều khả năng là do bạn đã sử dụng foreach thay vì for with indexer. Hoặc có thể bạn đã không sử dụng các thuộc tính thích hợp trong ràng buộc. Xem weblogs.asp.net/shijuvarghese/archive/2010/03/06/…
Erik Funkenbusch

Cảm ơn. Trên thực tế, khi tôi thử nó có nghĩa đen là @ Html.Serialize ("Model.ModelIDs", Model.ModelIDs) trong đó Model là ViewModel của tôi và nó có thuộc tính mảng ModelIDs int. Vì vậy, không có vòng lặp hoặc bất cứ điều gì. Khi biểu mẫu được gửi, các ModelID luôn rỗng trong ViewModel bị ràng buộc.
Alan Macdonald

@AlanMacdonald - Bạn không bao gồm "Model" trong tên.
Erik Funkenbusch

16

Đó là một chút hack, nhưng nếu @Html.EditorForhoặc @Html.DisplayForhoạt động cho danh sách của bạn, nếu bạn muốn đảm bảo rằng nó được gửi theo yêu cầu bài đăng nhưng không hiển thị, bạn có thể chỉ cần sử dụng display: none;để ẩn nó đi, ví dụ:

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

Điều này không lưu giá trị trong mô hình trên bài đăng của yêu cầu.
nldev

Nếu .EditorFor được thiết lập để hoạt động chính xác, thì tôi tin rằng điều này cũng sẽ hoạt động.
Mark Rhodes

9

Điều gì về việc sử dụng Newtonsoft để giải mã hóa đối tượng thành một chuỗi json và sau đó chèn nó vào trường Ẩn của bạn, ví dụ ( Model.DataResponse.Entity.CommissionDanh sách các đối tượng "CommissionRange" đơn giản như bạn sẽ thấy trong 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]
   }

Kết xuất dưới dạng:

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

Trong trường hợp của tôi, tôi thực hiện một số công cụ JS để chỉnh sửa json trong trường ẩn trước khi đăng lại

Trong bộ điều khiển của tôi, sau đó, tôi sử dụng lại Newtonsoft để deserialize:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);

Điều này đã làm việc cho tôi. Tôi nghĩ rằng giải pháp được chấp nhận sẽ sạch hơn nhiều.
e-on

6

Html.HiddenForchỉ được thiết kế cho một giá trị. Bạn sẽ cần tuần tự hóa danh sách của mình theo một cách nào đó trước khi tạo trường ẩn.

Ví dụ: nếu danh sách của bạn thuộc loại chuỗi, bạn có thể nối danh sách thành một danh sách được phân tách bằng dấu phẩy, sau đó chia danh sách sau khi đăng lại trong bộ điều khiển của bạn.


4

Tôi vừa phát hiện ra (sau vài giờ cố gắng tìm hiểu lý do tại sao các giá trị mô hình không quay trở lại bộ điều khiển) mà ẩn cho sẽ theo EditorFor.

Trừ khi tôi đang làm sai điều gì đó khác, đây là những gì tôi đã tìm thấy. Tôi sẽ không phạm sai lầm một lần nữa.

Trong ngữ cảnh của một Model có chứa danh sách của một lớp khác.

Điều này sẽ KHÔNG hoạt động:

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

Nơi như thế này SẼ ......

            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

Tôi đã bắt đầu tìm hiểu mã nguồn HiddenForvà tôi nghĩ rằng rào cản mà bạn đang thấy là đối tượng phức tạp của bạn MyListkhông hoàn toàn có thể chuyển đổi thành kiểu string, vì vậy khung công tác coi Modelgiá trị của bạn là nullvà làm cho valuethuộc tính trống.


3

Bạn có thể xem qua giải pháp này .

Chỉ đặt HiddenFor bên trong EditorTemplate.

Và trong Chế độ xem của bạn, hãy đặt điều này: @Html.EditorFor(model => model.MyList)

Nó sẽ hoạt động.


3

Đối mặt với cùng một vấn đề. Không có vòng lặp for, nó chỉ đăng phần tử đầu tiên của danh sách. Sau khi lặp qua vòng lặp for, nó có thể giữ danh sách đầy đủ và đăng thành công.

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

2

Một tùy chọn khác sẽ là:

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

Đây cũng là ý tưởng đầu tiên của tôi. Nhưng tôi đã có một mô hình chế độ xem, dự kiến ​​sẽ có một int [] cho trường MyList và chuỗi được phân tách bằng dấu phẩy không được phân tích cú pháp thành một mảng bằng cơ chế liên kết MVC.
Tadej Mali

2

Các foreachvòng lặp thay vì một forvòng lặp có thể là một giải pháp nhẹ sạch hơn.

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

1

Một cách khác có thể để khắc phục điều này là cung cấp cho mỗi đối tượng trong Danh sách của bạn một ID, sau đó sử dụng @Html.DropDownListFor(model => model.IDs)và điền vào một mảng chứa các ID.


1

có thể muộn, nhưng tôi đã tạo phương thức mở rộng cho các trường ẩn khỏi bộ sưu tập (với các mục kiểu dữ liệu đơn giản):

Vì vậy, đây là:

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

Cách sử dụng rất đơn giản như:

@Html.HiddenForCollection(m => m.MyList)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.