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

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

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

  • Как сохранить все 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>

person Ciarán Bruen    schedule 01.07.2012    source источник
comment
Я столкнулся с похожей ситуацией. Мне нужно было иметь раскрывающийся список, который позволял бы пользователю выбирать запись в графе объектов. Было 4 связанных таблицы, каждая из которых содержала примерно 5 полей. В итоге мне пришлось написать API-интерфейс javascript для деконструкции html, сгенерированного DropDownListFor, а затем заново изобрести раскрывающийся список вместо исходного. Я задал здесь пару вопросов, но так и не получил хороших ответов, и я чувствую, что DropDownListFor - это своего рода забытый пасынок в рамках.   -  person Travis J    schedule 02.07.2012


Ответы (5)


Все забывают «классический» способ решения этих проблем: использовать цикл 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>
person drzaus    schedule 28.01.2013
comment
если проверка выбора в представлении пахнет, вы всегда можете построить соответствующую ViewModel и соответствующий Partial. - person drzaus; 28.01.2013
comment
Я пробовал это, но он запрашивает ; точка с запятой после строки selected=selected. Intellisense показывает, что он все еще находится в контексте С#, а не в контексте html. Пожалуйста помоги! - person singsuyash; 16.04.2015
comment
@Suyash, может быть, попробовать использовать экранирование Razor <text>? weblogs.asp. net/scottgu/ - person drzaus; 17.04.2015
comment
Вы также можете принудительно перевести строку в Razor вместо C#, добавив @: впереди - person brichins; 06.11.2015
comment
@brichins Ага, @: — это сокращение от <text> взломанного. ком/архив/2011/01/06/ - person drzaus; 06.11.2015

У меня была аналогичная ситуация, когда мне нужно было передать третье значение каждому из элементов списка, чтобы определить действие, которое нужно выполнить в функции 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, который в конечном итоге заполнит раскрывающийся список. Это может быть в контроллере, а не в модели представления, я просто решил сделать его свойством моей модели представления.

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

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

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

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

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

person nikeaa    schedule 01.07.2012
comment
Я предполагаю, что вы анализируете исходный вывод Helper, чтобы избежать создания собственного TagBuilder и перезаписи всего волшебства, которое обычно происходит? - person drzaus; 28.01.2013
comment
@drzaus - да, эта строка делает это: var selectDoc = XDocument.Parse(htmlHelper.DropDownList(name, (IEnumerable‹SelectListItem›)selectList).ToString()); - person nikeaa; 08.02.2013
comment
Это замечательно. Однако использование GetFullHtmlFieldName привело к созданию DropDownList с двойными префиксами, т. е. ModelName_ModelName_MemberName. Мне пришлось изменить строку имени строки на просто: имя строки = ExpressionHelper.GetExpressionText(expression); - person friggle; 03.09.2013
comment
Я бы также улучшил код, проверив, имеет ли значение attribute.HtmlAttributes значение null внутри DropDownListWithItemAttributesFor. Кроме того, не рассматривали ли вы просто использование htmlHelper.DropDownListFor вместо htmlHelper.DropDownList, чтобы исключить необходимость определения имени? Я также добавил возможность передавать объект htmlAttributes для самого элемента select. - person Nick Bork; 10.10.2014

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

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

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

person Darin Dimitrov    schedule 01.07.2012
comment
Я не буду отправлять форму для вызова службы, я буду делать это на стороне клиента с помощью jQuery, поэтому мне нужно получить длинные имена при первоначальном заполнении раскрывающегося списка. - person Ciarán Bruen; 02.07.2012
comment
Что будет делать скрипт jQuery с этим значением? - person Darin Dimitrov; 02.07.2012
comment
Будет передан службе, использующей jQuery для получения дополнительных данных, например, $.ajax({url: myservice/?suggestWord= + carLongName + &callback=? - person Ciarán Bruen; 02.07.2012
comment
Разве вы не можете передать выбранный идентификатор этой службе и попросить службу получить соответствующее описание из базы данных? Но если вам действительно нужно это длинное описание в разметке, вы можете написать собственный html-помощник, который позволит вам это сделать. Встроенный помощник этого не делает. Я обновил свой ответ с предложением пользовательского помощника. - person Darin Dimitrov; 02.07.2012
comment
Спасибо за обновления. Служба, которую я использую, является удаленной службой, и я не имею над ней никакого контроля, поэтому мне нужно передать длинное имя. - person Ciarán Bruen; 02.07.2012

@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());
}
person Vladimir    schedule 03.02.2017

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

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

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;
}
person Ciarán Bruen    schedule 25.07.2012
comment
Синтаксический анализ XML значительно менее трудоемок, чем обращение сервера туда и обратно только для поиска значения. Но jQuery интереснее. - person drzaus; 28.01.2013
comment
разница в этих двух решениях не в весе, а на стороне клиента и на стороне сервера. - person friggle; 03.09.2013