Добавить атрибут для выбора списка
У меня есть список элементов в выпадающем списке в представлении 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;
}
}
Вот код, который я использую для создания раскрывающегося списка:
<div class="editor-field">
@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>
Ответы
Ответ 1
Просто вернемся к этому сейчас. Хотя ответ @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;
}
Ответ 2
Каждый забывает "классический" способ решения этих проблем: используйте цикл 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>
Ответ 3
У меня была аналогичная ситуация, когда мне нужно было передать третье значение каждому элементу списка, чтобы определить действие, которое нужно выполнить в функции jQuery. Вот мое решение (которое позволит вам добавить любое количество атрибутов для каждого элемента в раскрывающемся списке):
Сначала я создал класс SelectListItemWithAttributes следующим образом:
public class SelectListItemWithAttributes : SelectListItem {
public IDictionary<string, string> HtmlAttributes { get; set; }
}
Это позволяет мне создавать элементы для списка выбора с добавленными дополнительными атрибутами.
Во-вторых, я создал вспомогательный 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());
}
Это позволяет мне создать раскрывающийся список, используя новый класс 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;
}
}
Это строит список SelectListItemsWithAttributes, который в конечном итоге заполнит выпадающий список. Это может быть в контроллере вместо viewmodel, я просто решил сделать его свойством моей модели просмотра.
Наконец, в представлении это будет выглядеть так:
@Html.DropDownListWithItemAttributesFor(m => m.DropDownDataID, Model.DropDownDatas)
Это отобразит выпадающий список на странице с использованием свойства из модели просмотра, содержащей список SelectListItemsWithAttributes.
Я построил это решение из различных решений, которые я нашел в Интернете, поэтому для меня это было не совсем оригинально, но я собрал его во что-то, что сработало для меня.
Надеюсь, это поможет вам решить вашу проблему.
Ответ 4
Внутри действия контроллера, который должен получить форму отправки, вы можете использовать идентификатор выбранного значения для запроса своей базы данных, чтобы получить длинное отображаемое имя и делать все, что вы намеревались сделать с ним.
Помощник DropDownListFor
не поддерживает добавление атрибутов HTML5 data-*
к параметрам, но даже если это произойдет, они не будут отправлены как часть стандартного представления формы. Вам нужно будет использовать javascript для отправки их на сервер с помощью другой техники (скрытые поля, AJAX, параметры строки запроса...).
Но если по какой-то причине вам нужны дополнительные атрибуты в теге option, вы всегда можете написать пользовательский помощник.
Ответ 5
@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());
}