2012-06-14 3 views
16

Я использую ASP.NET MVC 3 и просто столкнулся с «gotcha» с помощью HTML-помощника DropDownListFor.ASP.NET MVC DropDownListДля выбора значения из модели

Я делаю это в моем контроллере:

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

И GetShippingTypes метод:

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

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

Причина, я положил его в ViewBag, а не в модели (я сильно типизированных модели каждый вид), заключается в том, что у меня есть набор элементов, который отображает с помощью EditorTemplate, который также должен получить доступ к списку выбора ShippingTypes.

В противном случае мне нужно пропустить всю коллекцию и назначить свойство ShippingTypes.

Пока все хорошо.

На мой взгляд, я делаю это:

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

(RequiredShippingTypeId имеет тип Int32)

Что происходит, что значение RequiredShippingTypeId является не выбран в раскрывающемся списке.

Я наткнулся на это: http://web.archive.org/web/20090628135923/http://blog.benhartonline.com/post/2008/11/24/ASPNET-MVC-SelectList-selectedValue-Gotcha.aspx

Он предполагает, что MVC будет искать выбранное значение из ViewData, когда список выбора составляет от ViewData. Я не уверен, что это так, потому что сообщение в блоге старое, и он говорит о бета-версии MVC 1.

Обходной, который решает этот вопрос заключается в следующем:

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

Я пытался не ToString на RequiredShippingTypeId в конце, что дает мне такое же поведение, как и раньше: Нет выбранного элемента.

Я думаю, что это проблема с типом данных. В конечном счете, HTML-помощник сравнивает строки (в списке выбора) с Int32 (от RequiredShippingTypeId).

Но почему она не работает при вводе SelectList в ViewBag - когда она прекрасно работает при добавлении его к модели, и делать это внутри вида:

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

Спасибо за обходной путь! Настолько неочевидно, что магия работает только с «простыми» лямбда-выражениями, и система не дает никаких предупреждений об этом. –

ответ

30

Причина, почему это не работает из-за ограничения в DropDownListFor помощника: он способен вывести выбранное значение с помощью лямбда-выражения, переданного в качестве первого аргумента только если это лямбда-выражение является простое выражение доступа к свойствам. Например, это не работает с выражениями доступа индексатора массива, который является вашим случаем из-за шаблона редактора.

Вы в основном имеют (за исключением шаблона редактора):

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

Следующая не поддерживается: m => m.ShippingTypes[i].RequiredShippingTypeId. Он работает только с простыми выражениями доступа к ресурсам, но не с индексированным доступом к коллекции.

Обходной путь, который вы нашли, является правильным способом решить эту проблему, явно передав выбранное значение при построении SelectList.

+0

Черт, я этого не знал. Я могу подтвердить, что это действительно так. Я попытался использовать «ShippingTypes» из «ViewBag», а не для модели для раскрывающегося списка, которая принимает общий тип доставки: '@ Html.DropDownListFor (m => m.Product.RequiredShippingTypeId, ViewBag.ShippingTypes as SelectList) '- и это работает. Итак, я думаю, я должен придерживаться уродливого обходного пути, мог бы написать HTML-помощник для этого, вместо того, чтобы оставлять грязные хаки в своих взглядах. – MartinHN

+0

@ Darin - можете ли вы указать мне на документацию для этого? Благодарю. – mokumaxCraig

+0

Это, похоже, также применяется, если вы используете какой-либо другой объект/псевдоним, чтобы получить значение, например. '@ Html.DropDownListFor (m => myViewModel.RequiredShippingTypeID, ...' Спасибо! – xr280xr

0

Это может быть глупо, но добавляет ли это к переменной в вашем представлении?

var shippingTypes = ViewBag.ShippingTypes; 

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

Нет, это не решит проблему. –

+0

Я никогда не пробовал, но я уверен, что @DarinDimitrov прав. – MartinHN

+0

k cool просто подумал, что я предлагаю это как возможное «более чистое» обходное решение. – Terry

0

Существует перегруженный метод для @ html.DropdownList для обработки этого. Существует альтернатива для установки выбранного значения в раскрывающемся списке HTML.

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

Мне удалось получить выбранное значение от модели.

"value", "text", Model.Section[b].State В данном разделе выше синтаксис добавляет выбранный атрибут значения, загруженного из контроллера

0

вы можете создать динамический ViewData вместо viewbag для каждого DROPDOWNLIST поля для комплексного типа. надеюсь, что это даст вам намек, как это сделать

 @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

Я был просто поражен этим ограничением и понял, простой обходной путь. Только определенный метод расширения, который внутренне генерирует SelectList с правильным выбранным элементом.

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

Самое лучшее, что это расширение может использоваться таким же образом, как оригинальный DropDownListFor:

@for(var i = 0; i < Model.Items.Count(); i++) 
{ 
    @Html.DropDownListForEx(x => x.Items[i].CountryId, Model.AllCountries) 
} 
Смежные вопросы