2012-06-14 12 views
16

Używam ASP.NET MVC 3, i po prostu uruchomiłem "gotcha" przy użyciu pomocnika HTML DropDownListFor.ASP.NET MVC DropDownListFor nie wybierając wartości z modelu

zrobić to w moim kontrolera:

ViewBag.ShippingTypes = this.SelectListDataRepository.GetShippingTypes(); 

i metoda GetShippingTypes:

public SelectList GetShippingTypes() 
{ 
    List<ShippingTypeDto> shippingTypes = this._orderService.GetShippingTypes(); 

    return new SelectList(shippingTypes, "Id", "Name"); 
} 

Przyczyny I umieścić go w ViewBag a nie w modelu (mam silnie typami modeli każdy widok), jest to, że mam kolekcję elementów, które renderuje przy użyciu EditorTemplate, który również musi uzyskać dostęp do listy wyboru ShippingTypes.

W przeciwnym razie muszę przechodzić przez całą kolekcję i przypisać właściwość ShippingTypes.

Jak dotąd tak dobrze.

Moim zdaniem to zrobić:

@Html.DropDownListFor(m => m.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList) 

(RequiredShippingTypeId jest typu Int32)

Co się dzieje, że wartość RequiredShippingTypeId jest nie wybrany w rozwijanym.

natknąłem to: http://web.archive.org/web/20090628135923/http://blog.benhartonline.com/post/2008/11/24/ASPNET-MVC-SelectList-selectedValue-Gotcha.aspx

Sugeruje on, że MVC będzie odnośnika wybraną wartość od ViewData, gdy lista wyboru jest od ViewData. Nie jestem pewien, czy tak już jest, ponieważ post na blogu jest stary, a on mówi o MVC 1 beta.

Rozwiązaniem, które rozwiązuje ten problem jest taki:

@Html.DropDownListFor(m => m.RequiredShippingTypeId, new SelectList(ViewBag.ShippingTypes as IEnumerable<SelectListItem>, "Value", "Text", Model.RequiredShippingTypeId.ToString())) 

nie próbował ToString na RequiredShippingTypeId na końcu, co daje mi takie samo zachowanie jak przed: Brak wybranego elementu.

Myślę, że to jest problem z typem danych. Ostatecznie pomocnik HTML porównuje łańcuchy znaków (na liście wyboru) z Int32 (od RequiredShippingTypeId).

Ale dlaczego to nie działa, gdy oddanie SelectList w ViewBag - kiedy to działa doskonale podczas dodawania go do modelu, a robi to w środku zdania:

@Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, Model.ShippingTypes) 
+0

Dzięki za obejście! Nie jest więc oczywiste, że magia działa tylko z "prostymi" wyrażeń lambda, a system nie ostrzega o tym. –

Odpowiedz

30

Powodem dlaczego to nie działa, to ze względu na ograniczenia DropDownListFor pomocnika: jest w stanie wywnioskować wybraną wartość za pomocą wyrażenia lambda przekazany jako pierwszy argument tylko jeśli to wyrażenie lambda jest proste wyrażenie dostępu do właściwości. Na przykład nie działa to w przypadku wyrażeń dostępu do indeksu tablicy, co jest twoim przypadkiem z powodu szablonu edytora.

Zasadniczo mam (z wyłączeniem szablonu edytora):

@Html.DropDownListFor(
    m => m.ShippingTypes[i].RequiredShippingTypeId, 
    ViewBag.ShippingTypes as IEnumerable<SelectListItem> 
) 

Poniższa nie jest obsługiwany: m => m.ShippingTypes[i].RequiredShippingTypeId. Działa tylko z prostymi wyrażeń dostępu do właściwości, ale nie z indeksowanym dostępem do kolekcji.

Rozwiązaniem, które znalazłeś, jest poprawny sposób rozwiązania tego problemu, jawnie przekazując wybraną wartość podczas budowania SelectList.

+0

Cholera, nie wiedziałem o tym. Mogę potwierdzić, że tak właśnie jest. Próbowałem użyć 'ShippingTypes' z' ViewBag', zamiast modelu dla rozwijanego, który przyjmuje ogólny typ wysyłki: '@ Html.DropDownListFor (m => m.Product.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList) '- i to działa. Sądzę więc, że muszę trzymać się tego brzydkiego rozwiązania, może napisać do tego pomocnika HTML zamiast zostawiać brudne sztuczki w moich widokach. – MartinHN

+0

@ Darin - czy możesz wskazać mi dokumentację? Dzięki. – mokumaxCraig

+0

Wydaje się to również mieć zastosowanie, jeśli używasz jakiegoś innego obiektu/aliasu, aby uzyskać wartość z np. '@ Html.DropDownListFor (m => myViewModel.RequiredShippingTypeID, ...' Dzięki! – xr280xr

0

To może być głupie, ale czy dodawanie go do zmiennej w twoim widoku robi cokolwiek?

var shippingTypes = ViewBag.ShippingTypes; 

@Html.DropDownListFor(m => m.Product.RequiredShippingTypeId, shippingTypes) 
+0

Nie, to nie rozwiąże problemu. –

+0

Nigdy tego nie próbowałem, ale jestem pewien, że @DarinDimitrov ma rację. – MartinHN

+0

k fajnie tylko pomyślałem, że zasugeruję to jako możliwe "czystsze" obejście problemu. – Terry

0

Istnieje przeciążona metoda dla @ html.DropdownList do obsługi tego. Istnieje alternatywa do ustawienia wybranej wartości na liście rozwijanej HTML.

@Html.DropDownListFor(m => m.Section[b].State, 
       new SelectList(Model.StatesDropdown, "value", "text", Model.Section[b].State)) 

Udało mi się uzyskać wybraną wartość z modelu.

"value", "text", Model.Section[b].State sekcja ta powyższa składnia dodaje wybrany atrybut do wartości obciążonej od kontrolera

0

można tworzyć dynamiczne ViewData zamiast viewbag dla każdego pola DropdownList dla typu złożonego. nadzieja to daje wskazówkę jak to zrobić

 @if (Model.Exchange != null) 
 
           { 
 
            for (int i = 0; i < Model.Exchange.Count; i++) 
 
            { 
 
             <tr> 
 
              @Html.HiddenFor(model => model.Exchange[i].companyExchangeDtlsId) 
 
              <td> 
 
                
 
               @Html.DropDownListFor(model => model.Exchange[i].categoryDetailsId, ViewData["Exchange" + i] as SelectList, " Select category", new { @id = "ddlexchange", @class = "form-control custom-form-control required" }) 
 
               @Html.ValidationMessageFor(model => model.Exchange[i].categoryDetailsId, "", new { @class = "text-danger" }) 
 
              </td> 
 
              <td> 
 
               @Html.TextAreaFor(model => model.Exchange[i].Address, new { @class = "form-control custom-form-control", @style = "margin:5px;display:inline" }) 
 
              
 
               @Html.ValidationMessageFor(model => model.Exchange[i].Address, "", new { @class = "text-danger" }) 
 
              </td> 
 
             </tr> 
 
            } 
 

 
           }

ViewModel CompanyDetail = companyDetailService.GetCompanyDetails(id); 
 
       if (CompanyDetail.Exchange != null) 
 
       for (int i = 0; i < CompanyDetail.Exchange.Count; i++) 
 
       { 
 
        ViewData["Exchange" + i]= new SelectList(companyDetailService.GetComapnyExchange(), "categoryDetailsId", "LOV", CompanyDetail.Exchange[i].categoryDetailsId); 
 
       }

0

właśnie uderzony przez to ograniczenie i zorientowali się proste obejście. Tylko zdefiniowana metoda rozszerzenia, która wewnętrznie generuje SelectList z poprawnym wybranym elementem.

public static class HtmlHelperExtensions 
{ 
    public static MvcHtmlString DropDownListForEx<TModel, TProperty>(
     this HtmlHelper<TModel> htmlHelper, 
     Expression<Func<TModel, TProperty>> expression, 
     IEnumerable<SelectListItem> selectList, 
     object htmlAttributes = null) 
    { 
     var selectedValue = expression.Compile().Invoke(htmlHelper.ViewData.Model); 
     var selectListCopy = new SelectList(selectList.ToList(), nameof(SelectListItem.Value), nameof(SelectListItem.Text), selectedValue); 

     return htmlHelper.DropDownListFor(expression, selectListCopy, htmlAttributes); 
    } 
} 

Najlepszą rzeczą jest to, że rozszerzenie to może być stosowane w ten sam sposób jak oryginał DropDownListFor:

@for(var i = 0; i < Model.Items.Count(); i++) 
{ 
    @Html.DropDownListForEx(x => x.Items[i].CountryId, Model.AllCountries) 
} 
Powiązane problemy