programing tip

@ Html.HiddenFor는 ASP.NET MVC의 목록에서 작동하지 않습니다.

itbloger 2020. 9. 7. 07:52
반응형

@ Html.HiddenFor는 ASP.NET MVC의 목록에서 작동하지 않습니다.


List를 속성으로 포함하는 모델을 사용하고 있습니다. 이 목록을 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 문자열로 역 직렬화 한 다음 숨겨진 필드에 삽입하는 것은 어떻습니까? 예를 들어 ( Model.DataResponse.Entity.CommissionJSON에서 볼 수있는 간단한 "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);

Html.HiddenFor하나의 값만을 위해 설계되었습니다. 숨겨진 필드를 만들기 전에 어떤 방식 으로든 목록을 직렬화해야합니다.

예를 들어, 목록이 문자열 유형 인 경우 목록을 쉼표로 구분 된 목록으로 결합한 다음 컨트롤러에서 다시 게시 한 후 목록을 분할 할 수 있습니다.


I've just found out (after a couple of hours of trying to figure out why model values weren't going back to the controller) that hidden for should follow the EditorFor.

Unless I am doing something else wrong this is what I found. I will not make the mistake again.

In the context of a Model that contains a list of another class.

This will NOT work:

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

Where as this WILL......

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

I started digging through the source code for HiddenFor, and I think the roadblock you're seeing is that your complex object MyList is not implicitly convertible to type string, so the framework treats your Model value as null and renders the value attribute empty.


You can take a look on this solution.

Put only HiddenFor inside the EditorTemplate.

And in your View put this: @Html.EditorFor(model => model.MyList)

It should works.


Faced the same issue. Without for loop, it only posted the first element of the list. After iterating through for loop, it can keep full list and post successfully.

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

Another option would be:

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

Another possible way to fix this would be to give each object in your List an ID, then use @Html.DropDownListFor(model => model.IDs) and populate an array which holds the IDs.


maybe late, but i created extension method for hidden fields from collection (with simple data type items):

So here it is:

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

Usage is as simple as:

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

The foreach loop instead of a for loop might be a slightly cleaner solution.

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

참고URL : https://stackoverflow.com/questions/9385286/html-hiddenfor-does-not-work-on-lists-in-asp-net-mvc

반응형