2012-02-21 12 views
63

Używam modelu, który zawiera listę jako właściwość. Zapełniam tę listę przedmiotami, które pobieram z SQL Server. Chcę, aby lista była ukryta w widoku i przekazana do akcji POST. Później mogę chcieć dodać więcej elementów do tej listy za pomocą jQuery, która czyni tablicę nieodpowiednią do późniejszego rozszerzenia. Zwykle można użyć tej funkcji, aby wykonać tę funkcję, ale z jakiegoś powodu lista w POST ma zawsze wartość NULL.@ Html.HiddenFor nie działa na listach w ASP.NET MVC

Bardzo proste pytanie, czy ktoś wie, dlaczego MVC zachowuje się tak?

+0

Zazwyczaj nie można ukryć całych list w ten sposób. Jaki jest twój pożądany wynik pod względem '' s? –

+0

Co zawiera słowo "MyList"? 'HiddenFor' jest używane tylko dla jednego wejścia na raz. –

+0

Co to jest "Model.MyList"? Konieczne może być ręczne wykonanie szeregowania/deserializacji na liście. –

Odpowiedz

91

Właśnie natknąć się ten problem i rozwiązać go po prostu w następujący sposób:

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

za pomocą bo zamiast foreach model wiążący będzie działać prawidłowo i odebrać wszystkich ukrytych wartości na liście. Wydaje się najprostszym sposobem rozwiązania tego problemu.

+0

Dzięki! Uratowałem moją noc. – TSmith

+5

Dzięki - ładne proste rozwiązanie. Potrzebny jest jednak tylko mały modyfikator: pole identyfikatora obiektu musi zostać określone. Więc jeśli pole nazywa się RowId, wtedy: '@ Html.HiddenFor (model => Model.ToGroups [i] .RowId)' –

+2

pracował dla mnie, nawet gdy miałem wiele pól w modelach w kolekcji. To znaczy. '@ Html.EditorFor (model => Model.ToGroups [i] .Id)', po którym następuje '@ Html.EditorFor (model => Model.ToGroups [i] .Description)' następnym razem - zarówno w wersji for- pętla. I kontroler był w stanie zmapować go do listy modeli z tymi polami. Aby upewnić się, że nic z tego nie pojawiło się na ekranie, po prostu otocz go w '

' – mmcrae

5

Html.HiddenFor jest przeznaczony tylko dla jednej wartości. Przed utworzeniem ukrytego pola będziesz musiał serializować swoją listę.

Na przykład, jeśli twoja lista jest typu ciąg, możesz dołączyć do listy na listę rozdzielaną przecinkami, a następnie podzielić listę po powrocie do kontrolera.

27

HiddenFor nie jest jak DisplayFor lub EditorFor. Nie będzie działać z kolekcjami, tylko z pojedynczymi wartościami.

Można użyć pomocnika Serializować HTML dostępne w projekcie MVC Futures do serializacji obiektu do pola ukrytego, albo trzeba będzie napisać kod siebie. Lepszym rozwiązaniem jest po prostu serializacja pewnego rodzaju identyfikatora i ponowne pobranie danych z bazy danych podczas odświeżania.

+0

Czy masz przykład ? Próbowałem tego i nie udało się powiązać z wartością ViewModel po przesłaniu formularza. –

+0

@AlanMacdonald - jeśli coś nie uda się powiązać, to dlatego, że twoje nazewnictwo nie jest poprawne, bardziej niż prawdopodobne, ponieważ użyłeś foreach zamiast for z indeksem. A może nie użyłeś odpowiednich atrybutów w wiązaniu. Zobacz http://weblogs.asp.net/shijuvarghese/archive/2010/03/06/persisting-model-state-in-asp-net-mvc-using-html-serialize.aspx –

+0

Dzięki. Właściwie kiedy próbowałem to było dosłownie @ Html.Serialize ("Model.ModelIDs", Model.ModelIDs), gdzie Model był moim ViewModel i miał właściwość tablicyID ModelID. Więc nie było żadnych pętli ani niczego. Po przesłaniu formularza identyfikatory modelu zawsze miały wartość zerową w powiązanym module ViewModel. –

1

zacząłem kopać za pośrednictwem kodu źródłowego HiddenFor i myślę, że blokada widzisz jest to, że kompleks nie jest przedmiotem MyList niejawnie zamienny wpisać string, więc ramy traktuje swoją wartość Model jak null i renderuje value atrybut pusty.

2

Możesz rzucić okiem na to solution.

Umieść tylko HiddenFor wewnątrz EditorTemplate.

I w widoku umieścić to: @Html.EditorFor(model => model.MyList)

powinien to działa.

12

Jest to bit hack, ale jeśli @Html.EditorFor lub @Html.DisplayFor praca na liście, jeśli chcesz, aby upewnić się, że wysyłane na żądanie POST, ale nie jest widoczny, można po prostu projektować je do korzystania display: none; ukryć go zamiast, np:

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

To nie zapisuje wartości w modelu na stanowisku żądania. – nldev

+0

Jeśli .EditorFor jest skonfigurowany do poprawnego działania, to też powinno działać tak, jak sądzę. –

0

Innym możliwym sposobem rozwiązania tego byłoby dać każdy obiekt na liście identyfikator, a następnie użyć @Html.DropDownListFor(model => model.IDs) i wypełnić tablicę, która przechowuje identyfikatory.

3

Właśnie dowiedziałem się (po kilku godzinach próbuje dowiedzieć się, dlaczego modelu wartości nie wracali do regulatora), które ukryte powinny być zgodne z EditorFor.

Chyba że robię coś złego, to właśnie znalazłem. Nie popełnię znowu błędu.

W kontekście modelu zawierającego listę innej klasy.

to nie będzie działać:

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

Gdzie jak to będzie ......

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

Co na temat korzystania Newtonsoft deserializacji obiektu na ciąg json i włóż to pod twoje ukryte pole np (Model.DataResponse.Entity.Commission jest Lista prostego "CommissionRange" obiektów jak zobaczysz w 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] 
    } 

Renders jak:

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

W moim przypadku robię trochę rzeczy JS, żeby edytować json w ukrytym polu przed opublikowaniem w moim kontrolerze używać Newtonsoft ponownie deserializowania:

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

To działało dla mnie. Myślałem, że jest o wiele czystsze, przyjęte rozwiązanie. –

0

może późno, ale stworzył metodę rozszerzenia dla ukrytych pól z kolekcji (z prostych elementów typu danych):

Więc to jest tutaj:

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

Użycie jest tak proste, jak:

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

Wobec tego samego problemu. Bez pętli for, opublikował tylko pierwszy element listy. Po przejściu przez pętlę for, może zachować pełną listę i wysłać ją z powodzeniem.

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