Сохранение выбранного значения в MVC DropDownLists - PullRequest
1 голос
/ 23 октября 2009

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

Я пытаюсь сделать "создать" Действие и Просмотр. Мой ввод данных относительно прост и распространен: у меня есть выпадающий список и текстовое поле. В моем случае я создаю канал контактов пользователя, и в раскрывающемся списке выбирается электронная почта и textmsg, а затем в текстовое поле вводится соответствующая контактная информация, либо правильно сформированный адрес электронной почты, либо номер мобильного телефона.

Вот (слегка упрощенная форма) моей страницы просмотра:

 <tr>
     <td><%= Html.DropDownList("ChannelDescription", Model.ChannelDescription, "Select a Channel", new { id = "ChannelDDL", onchange="ChannelDDLChanged()" })%>
         <br />
         <%= Html.ValidationMessage("ChannelDescription", "Please Select a Channel") %>
      </td>
      <td>
           <%= Html.TextBox("SubscriberNotificationAddr") %> <br />
           <%= Html.ValidationMessage("SubscriberNotificationAddr", "Please enter a contact address or number") %>
       </td>
  </tr>

Я использую строго типизированную модель ViewData, а не ViewDataDictionary. Элемент ChannelDescription представляет собой список SelectList, который инициализируется списком вариантов, а не выбором.

Начальное отображение формы, ввод данных в форму и извлечение данных из формы контроллером проходит нормально.

Моя проблема заключается в том, что если данные содержат ошибку, такую ​​как неверно сформированный адрес электронной почты или номер мобильного телефона, и мне нужно вернуться к представлению, мне не удалось повторно отобразить выбранный список. Элемент ChannelDescription воссоздается в контроллере с выбором пользователя в качестве выбранного элемента. Я установил точки останова в этой строке представления и убедился, что для выбранного элемента списка элементов свойство Selected установлено в true, но оно по-прежнему отображает значение «Выбрать канал» по умолчанию.

Похоже, что это будет очень распространенная ситуация, и она не должна быть такой сложной. Что я делаю не так?

К вашему сведению, это с MVC 1.0 (Release), Windows 7 и VS 2008, работающими под Firefox 3.5.2.

Ответы [ 3 ]

1 голос
/ 24 октября 2009

После просмотра ответа выше я хотел проверить его, потому что все примеры, которые я видел, действительно использовали ViewDataDictionary, а не строго типизированный ViewDataModel.

Итак, я провел несколько экспериментов. Я построил очень простое представление, которое использовало обычный ViewDataDictionary и передавало значения по именованным ключам. Сохранился выбранный пункт просто отлично. Затем я вырезал и вставил этот View (и контроллер) в другой, изменив только то, что было необходимо для переключения на строго типизированную модель ViewData. И вот, он также сохранил выбранный пункт.

Так что же отличалось между моим простым тестом и моим приложением? В моем тесте я использовал просто "Html.DropDownList (" name "," optionLabel ")". Однако в моем приложении мне нужно было добавить атрибуты HTML, и единственные доступные перегрузки, которые включали атрибуты HtmlAttributes, также включают список выбора.

Оказывается, перегрузка DropDownList с параметром списка выбора прервана! Глядя на загруженный исходный код MVC, когда DropDownList вызывается только с именем или именем и optionLabel, он заканчивает тем, что получает целевой список выбора из ViewData, а затем вызывает закрытый метод SelectInternal следующим вызовом:

    return SelectInternal(htmlHelper, optionLabel, name, selectList, true /* usedViewData */, false /* allowMultiple */, (IDictionary<string, object>)null /* htmlAttributes */);

Однако, если он вызывается с параметром selectList, он заканчивается следующим:

   return SelectInternal(htmlHelper, optionLabel, name, selectList, false /* usedViewData */, false /* allowMultiple */, htmlAttributes);

Разница в том, что в первом (который будет работать правильно) параметр usedViewData имеет значение true, а во втором - false. Что на самом деле нормально, но обнаруживает внутренний дефект в процедуре SelectInternal.

Если usedViewData имеет значение false, он получает объектную переменную "defaultValue" из модели ViewData. Однако defaultValue используется, как если бы это была либо строка, либо массив строк, тогда как фактически то, что возвращается из ViewData, представляет собой SelectList. (IEnumerable<SelectListItem>).

Если usedViewData имеет значение true, тогда defaultValue будет иметь значение null или строку.

Тогда, если defaultValue не равно NULL, он в конечном итоге попадает в блок кода, который содержит это:

        foreach (SelectListItem item in selectList) {
            item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
            newSelectList.Add(item);

selectList - это исходный список selectList, который был передан, поэтому элемент представляет собой SelectListItem (строка Text, строка Value и bool Selected). Но selectedValues ​​был получен из defaultValue и становится списком списков выбора, а не списком строк. Таким образом, для каждого из элементов он устанавливает флаг Selected в зависимости от того, содержит ли selectedValues ​​список item.Value. Что ж, список SelectLists никогда не собирается «содержать» строку, поэтому item.Selected никогда не устанавливается. (Исправление: на самом деле, после более подробной трассировки с помощью отладчика, я обнаружил, что selectedValues ​​наследуется от defaultValue с помощью вызова ToString (). Таким образом, на самом деле это список строк, но вместо того, чтобы содержать нужные значения, содержит "System.Web.Mvc.SelectList" - результат применения ToString () "к сложному объекту, например, SelectList. Результат все тот же - мы не собираемся находить искомое значение в этом список.)

Затем он заменяет недавно созданный "newSelectList" на оригинальный "selectList" и продолжает строить HTML из него.

Как сказал выше cagdas (я прошу прощения за разделку вашего имени, но я не знаю, как сделать эти символы на моей клавиатуре США), я думаю, мне придется создать свой собственный метод для использования вместо DropDownList HtmlHelper. Я полагаю, что поскольку этот выпуск 1 и выпуск 2 находятся в бета-версии 2, мы не можем ожидать каких-либо исправлений ошибок, если сами не сделаем это правильно?

Кстати, если вы следили за мной так далеко, этот код находится в src \ SystemWebMvc \ Mvc \ Html \ SelectExtensions.cs, около строки 116-136

1 голос
/ 04 ноября 2009

У меня были некоторые обсуждения с Брэдом Уилсоном, из команды MVC, и он объяснил мне, что я неправильно понимаю, как должен использоваться вспомогательный метод DropDownList (недоразумение, которое я думаю, может быть довольно распространенным, из того, что я прочитал ).

По сути, ЛИБО передайте ему SelectList в именованном параметре ViewModel, и пусть он построит выпадающий список из этого списка с выбранными соответствующими элементами, ИЛИ передайте ему SelectList в качестве отдельного параметра и разрешите именованный параметр ViewModel будет просто строкой значений для выбранного элемента (ов). Если вы зададите ему параметр SelectList, то он ожидает, что именованное значение будет строкой или списком строк, а НЕ SelectList.

Итак, теперь ваша ViewModel имеет два элемента для одного концептуального элемента в представлении (выпадающий список). Таким образом, у вас может быть модель, которая имеет

string SelectedValue {get; set;}
SelectList DropDownElements { get; set;}

Затем вы можете предварительно заполнить DropDownElements вариантами, но в привязке вида вашей модели вам просто нужно иметь дело с элементом SelectedValue. Мне кажется, это очень хорошо работает, когда я так делаю.

0 голосов
/ 23 октября 2009

Да, у меня тоже было очень много проблем с тем, чтобы DropDownList уважал выбранный элемент, который я ему дал.

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

К вашему сведению, я перестал использовать этот метод HtmlHelper. Теперь я просто сам выводю теги <select> и <option> с помощью цикла и устанавливаю свойство selected тега option, проверяя его самостоятельно.

...