Добавить атрибут для выбора списка

У меня есть список элементов в выпадающем списке в представлении Razor. В базе данных каждый элемент имеет 3 значения, связанные с ним: идентификатор базы данных, краткое имя (для отображения) и длинное имя (для перехода к службе). В раскрывающемся списке должно отображаться краткое имя, поэтому я заполняю раскрывающийся список идентификатором базы данных как значение и краткое имя в качестве текста.

Однако, когда пользователь выбирает элемент, мне нужно передать длинное имя в качестве параметра запроса службе поиска с помощью jQuery, например, когда Cortina будет пропущена, "Ford Cortina 1979 Blue" необходимо передать в службу. Моя первая мысль - хранить длинное имя как атрибут dash данных, но мне интересно, есть ли лучший способ. Так

  • Как сохранить все 3 значения в раскрывающемся списке?
  • Если я использую атрибуты тишины данных, как включить все значения LONG_NAME в Html.DropDownListFor или каким-то образом добавить их в раскрывающийся список?

DB:

CARID SHORT_NAME LONG_NAME
1 Viper Dodge Viper 1982
2 Boxster Porsche Boxster 2009 Black
3 Cortina Ford Cortina 1979 Blue

Помощник контроллера для создания раскрывающегося списка:

public static IEnumerable<selectlistitem> GetSelectList(this IEFRepository repository, string typeName)
{
 var vehicle = repository.TypeTypes.FirstOrDefault(t => t.Name.ToUpper() == typeName);
 if (vehicle != null)
 {
 var carList = vehicle.SubTypes.ToList().OrderBy(s => s.Name);
 var selectList = new SelectList(subTypeList, "SubTypeID", "Name");
 return selectList;
 }
}
</selectlistitem>

Вот код, который я использую для создания раскрывающегося списка:

<div>
 @Html.DropDownListFor(model => model.CarID,
 new SelectList(ViewBag.Cars, "Value", "Text", "1"))
 @Html.ValidationMessageFor(model => model.CarShortName)
</div>

Здесь вывод:

<select id="CarID" name="CarID" data-val="true" data-val-number="The field CarID must be a number." data-val-required="The CarID field is required.">
 <option value="2">Boxster</option>
 <option value="3">Cortina</option>
 <option selected="selected" value="1">Viper</option>
</select>
5 ответов

Просто вернемся к этому сейчас. Хотя ответ @nikeaa, безусловно, является жизнеспособным решением, я подумал, что это был немного тяжеловес, особенно используя XDocument. Напомним, что я имею дело с TypeType (Cars) и SubType (список типов автомобилей - Viper, Granada, Hunter, Zodiac, Wolsley 1660 и т.д.). TypeType может также быть грузовыми автомобилями, велосипедами и т.д. Итак, вот как я решил это:

Я добавил JsonResult метод на контроллер, чтобы вернуть анонимный объект с 3 свойствами, которые я хотел:

public class VehicleController : Controller
{
 // etc.
 public JsonResult GetSubTypesForTypeType(string typeTypeName)
 {
 var cars = pronova2Repository.GetTypeWithSubTypes(typeTypeName);
 return cars == null
 ? Json(new object[0], JsonRequestBehavior.AllowGet)
 : Json(cars.SubTypes.OrderBy(s => s.Name).Select(
 s => new { s.SubTypeID, s.Name, s.Description }).ToArray(),
 JsonRequestBehavior.AllowGet);
 }
 // etc.
}

Тогда в js:

Заполните раскрывающийся список:

// populate the cars drop down when the select list is available
if ($('select#SubTypeID').length) {
 var carsSelect = $('select#SubTypeID');
 var carsList = populateCarsList("CARS");
 var carsListHtml = createCarsSelectList(carsList);
 carsSelect.html('');
 carsSelect.append(carsListHtml);
 $('#SubTypeID').change(function (e) {
 clearFormData();
 });
}

Вызвать функцию для получения подтипов (автомобилей) с помощью вызова ajax:

function populateCarsList(typeTypeName) {
 var carsList;
 $.ajax({
 url: '/Vehicle/GetSubTypesForTypeType',
 data: { typeTypeName: typeTypeName },
 async: false
 }).done(function (data) {
 carsList = data;
 }).error(function (msg, url, line) {
 alert("Error retrieving cars from Vehicle/GetSubTypesForTypeType. Error message: " + line);
 });
 return carsList;
}

Функция создания списка выбора с добавленным описанием в качестве атрибута "data- *":

function createCarsSelectList(selectData) {
 var html = '',
 len = selectData.length,
 selected,
 description;
 for (var i = 0; i < len; i++) {
 // "Viper" should be selected by default
 if (selectData[i].Name.toLocaleUpperCase() === "VIPER") {
 selected = ' selected="selected" ';
 } else {
 selected = '';
 }
 // Add the description (as a "data-" attribute), some descritions are null
 if (selectData[i].Description != null) {
 description = selectData[i].Description;
 } else {
 description = '';
 }
 html += '<option value="' + selectData[i].SubTypeID + '" data-description="' + description + '" '="" +="" selected>' + selectData[i].Name + '</option>';
 }
 return html;
}


Каждый забывает "классический" способ решения этих проблем: используйте цикл foreach и на самом деле напишите входной html. Единственный недостаток заключается в том, что вы должны добавить атрибут автоматического атрибута (например, валидация и т.д.), Который в зависимости от вашей цели не может быть большой проблемой.

Что-то вроде:

<select> // add other attributes as expected
@foreach(var type in Model.MyFancyTypes) {
<option value="@type.SubTypeID" data-description="@type.Description" @if(viewbag.typeselected="=" type.subtypeid)="" {="" selected="selected" }="">@type.Name</option>
}
</select>


У меня была аналогичная ситуация, когда мне нужно было передать третье значение каждому элементу списка, чтобы определить действие, которое нужно выполнить в функции jQuery. Вот мое решение (которое позволит вам добавить любое количество атрибутов для каждого элемента в раскрывающемся списке):

Сначала я создал класс SelectListItemWithAttributes следующим образом:

public class SelectListItemWithAttributes : SelectListItem {
 public IDictionary<string, string=""> HtmlAttributes { get; set; }
 }
</string,>

Это позволяет мне создавать элементы для списка выбора с добавленными дополнительными атрибутами.

Во-вторых, я создал вспомогательный HTML-метод под названием DropDownListWithItemAttributesFor следующим образом:

public static MvcHtmlString DropDownListWithItemAttributesFor<tmodel, tvalue="">(this HtmlHelper<tmodel> htmlHelper, Expression<func<tmodel, tvalue="">> expression, IEnumerable<selectlistitemwithattributes> selectList) {
 string name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); 
 var selectDoc = XDocument.Parse(htmlHelper.DropDownList(name, (IEnumerable<selectlistitem>)selectList).ToString());
 var options = from XElement el in selectDoc.Element("select").Descendants()
 select el;
 for (int i = 0; i < options.Count(); i++){
 var option = options.ElementAt(i);
 var attributes = selectList.ElementAt(i);
 foreach (var attribute in attributes.HtmlAttributes){
 option.SetAttributeValue(attribute.Key, attribute.Value);
 }
 }
 selectDoc.Root.ReplaceNodes(options.ToArray());
 return MvcHtmlString.Create(selectDoc.ToString());
}
</selectlistitem></selectlistitemwithattributes></func<tmodel,></tmodel></tmodel,>

Это позволяет мне создать раскрывающийся список, используя новый класс SelectListWithAttributes в качестве атрибутов. В принципе, он создает HTML для выпадающего списка, анализирует его в документе XML, а затем добавляет любые элементы в массиве HtmlAttributes в качестве дополнительных атрибутов для каждого элемента в раскрывающемся списке.

В-третьих, в моем коде ViewModel у меня есть следующее:

private List<selectlistitemwithattributes> pDropDownDatas = null;
public List<selectlistitemwithattributes> DropDownDatas {
 get {
 var DropDownDataItems = (
 from c in db.GetDropDownDataList(1, 1000)
 where c.AccountTypeID == this.AccountTypeID
 select new SelectListItemWithAttributes() { Text = c.Title, Value = c.ID.ToString(), HtmlAttributes = new Dictionary<string, string=""> { { "data-callback", c.RequiresCallback.ToString().ToLower() } } } ).ToList()
 ;
 DropDownDataItems.Insert(0, new SelectListItemWithAttributes() { Text = "-- Select --", Value = "", HtmlAttributes = new Dictionary<string, string=""> { { "data-callback", "false" } } });
 return DropDownDataItems;
 }
}
</string,></string,></selectlistitemwithattributes></selectlistitemwithattributes>

Это строит список SelectListItemsWithAttributes, который в конечном итоге заполнит выпадающий список. Это может быть в контроллере вместо viewmodel, я просто решил сделать его свойством моей модели просмотра.

Наконец, в представлении это будет выглядеть так:

@Html.DropDownListWithItemAttributesFor(m => m.DropDownDataID, Model.DropDownDatas)

Это отобразит выпадающий список на странице с использованием свойства из модели просмотра, содержащей список SelectListItemsWithAttributes.

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

Надеюсь, это поможет вам решить вашу проблему.


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

Помощник DropDownListFor не поддерживает добавление атрибутов HTML5 data-* к параметрам, но даже если это произойдет, они не будут отправлены как часть стандартного представления формы. Вам нужно будет использовать javascript для отправки их на сервер с помощью другой техники (скрытые поля, AJAX, параметры строки запроса...).

Но если по какой-то причине вам нужны дополнительные атрибуты в теге option, вы всегда можете написать пользовательский помощник.


@nikeaa Спасибо за ваш код. Я нашел несколько проблем с ним (например, когда список опций пуст, выбор не отображается правильно, вам не нужно заменять параметры, просто изменять их, в противном случае удаляются некоторые атрибуты выбора), и я добавил некоторые дополнительные параметры, чтобы полностью использовать мощность DropDownListFor. Вот моя версия метода:

public static MvcHtmlString DropDownListWithItemAttributesFor<tmodel, tproperty="">(
 this HtmlHelper<tmodel> htmlHelper, Expression<func<tmodel, tproperty="">> expression, IEnumerable<selectlistitemwithattributes> selectList,
 string optionLabel, IDictionary<string, object=""> htmlAttributes)
{
 if (selectList == null || !selectList.Any())
 return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes);
 var selectDoc = XDocument.Parse(htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes).ToString());
 var options = selectDoc.Element("select").Descendants().ToArray();
 for (int i = 0; i < options.Length; i++)
 {
 var option = options[i];
 var attributes = selectList.ElementAt(i);
 foreach (var attribute in attributes.Attributes)
 option.SetAttributeValue(attribute.Key, attribute.Value);
 }
 return MvcHtmlString.Create(selectDoc.ToString());
}
</string,></selectlistitemwithattributes></func<tmodel,></tmodel></tmodel,>

licensed under cc by-sa 3.0 with attribution.