17

我在 Razor 视图的下拉列表中有一个项目列表。在数据库中,每个项目都有 3 个与之关联的值 - 数据库 ID、短名称(用于显示)和长名称(用于传递给服务)。下拉列表必须显示短名称,因此我使用数据库 id 作为值和短名称作为文本填充下拉列表。

然而,当用户选择一个项目时,我需要将长名称作为查询参数传递给使用 jQuery 的搜索服务,例如当 Cortina 被选中时,需要将“Ford Cortina 1979 Blue”传递给该服务。我的第一个想法是将长名称存储为数据破折号属性,但我想知道是否有更好的方法。所以

  • 如何将所有 3 个值存储在下拉列表中?
  • 如果我确实使用数据破折号属性,我如何将所有 LONG_NAME 值合并到 Html.DropDownListFor 或以某种方式将它们添加到下拉列表中?

D B:

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>
4

5 回答 5

29

每个人都忘记了解决这些问题的“经典”方法:使用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>
于 2013-01-28T19:33:07.773 回答
22

我有类似的情况,我需要将第三个值传递给每个列表项,以确定在 jQuery 函数中采取的操作。这是我的解决方案(这将允许您为下拉列表中的每个项目添加任意数量的属性):

首先,我创建了一个 SelectListItemWithAttributes 类,如下所示:

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

这允许我为选择列表创建带有附加属性的项目。

其次,我创建了一个名为 DropDownListWithItemAttributesFor 的 HTML 辅助方法,如下所示:

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 列表的视图模型中的属性在页面上显示下拉列表。

我根据在互联网上找到的各种解决方案构建了这个解决方案,所以它并不是我的全部原创,但我将它们组合成对我有用的东西。

希望这能帮助您解决您的问题。

于 2012-07-01T21:54:39.707 回答
8

在应该接收表单提交的控制器操作中,您可以使用所选值的 id 来查询您的数据库,以获取长显示名称并执行您打算使用它执行的任何操作。

DropDownListFor帮助程序不支持向选项添加 HTML5属性,data-*但即使这样做,它们也不会作为标准表单提交的一部分发送。您必须使用 javascript 使用另一种技术(隐藏字段、AJAX、查询字符串参数...)将它们发送到服务器。

但是,如果出于某种原因,您需要在选项标签上添加其他属性,您总是可以编写自定义帮助程序。

于 2012-07-01T20:24:31.800 回答
3

@nikeaa 感谢您的代码。I found a few issues with it (eg when the option list is empty, the select is not rendered correctly; you don't need to replace the options, just modify them, otherwise some attributes of the select are removed) and I added some附加参数以充分利用 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());
}
于 2017-02-03T14:56:57.243 回答
1

现在才回到这个话题。虽然@nikeaa 的答案肯定是一个可行的解决方案,但我认为它有点重量级,尤其是使用 XDocument。提醒一下,我正在处理的是 TypeType(汽车)和 SubType(汽车类型列表 - Viper、Granada、Hunter、Zodiac、Wolsley 1660 等)。TypeType 也可以是 Trucks、Bicycles 等。这就是我解决它的方法:

我在 Controller 上添加了一个 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;
}
于 2012-07-25T13:35:30.060 回答