You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core MVC自定义HTML助手下拉框模型绑定失败求助

问题:ASP.NET Core MVC自定义下拉按钮控件无法绑定模型

问题原因

  1. 你使用了type="button"的input控件,这类按钮的值不会随表单提交到后台,模型绑定自然无法获取到选中值
  2. 下拉项用的是<li>标签,没有关联到可提交的表单控件,点击选项时也未更新用于提交的字段值
  3. 原代码存在多处语法错误:dropdown未加引号、string.empty应为string.Empty、三元运算符写错(>替换为?)、调用BuildDropdownList时传的id变量未定义(应使用fieldId)

修正后的代码实现

BootstrapHtml.cs

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System.IO;
using System.Linq.Expressions;
using System.Text.Encodings.Web;

public static class BootstrapHtml
{
    public static IHtmlContent BootstrapDropdownFor<TModel, TValue>(
        this IHtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TValue>> expression,
        IEnumerable<SelectListItem> list)
    {
        var expressionHelper = htmlHelper.ViewContext.HttpContext.RequestServices.GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;
        var fieldName = expressionHelper.GetExpressionText(expression);
        var fullBindingName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
        var fieldId = TagBuilder.CreateSanitizedId(fullBindingName, "-");
        var metaData = expressionHelper.CreateModelExpression(htmlHelper.ViewData, expression);
        var selectedValue = metaData.Model?.ToString() ?? string.Empty;
        var selectedText = list.FirstOrDefault(item => item.Value == selectedValue)?.Text ?? "请选择";

        // 隐藏输入框:负责提交选中值,是模型绑定的核心
        var hiddenInput = new TagBuilder("input")
        {
            Attributes =
            {
                { "id", $"{fieldId}-hidden" },
                { "name", fullBindingName },
                { "type", "hidden" },
                { "value", selectedValue }
            }
        };

        // 下拉触发按钮:仅用于打开菜单,不参与表单提交
        var dropdownButton = new TagBuilder("button")
        {
            Attributes =
            {
                { "id", fieldId },
                { "type", "button" },
                { "class", "btn btn-secondary dropdown-toggle" },
                { "data-bs-toggle", "dropdown" },
                { "aria-expanded", "false" }
            }
        };
        dropdownButton.InnerHtml.Append(selectedText);

        var wrapper = new TagBuilder("div");
        wrapper.AddCssClass("dropdown");

        wrapper.InnerHtml.AppendHtml(hiddenInput);
        wrapper.InnerHtml.AppendHtml(dropdownButton);
        wrapper.InnerHtml.AppendHtml(BuildDropdownList(fieldId, list, selectedValue));

        using (var writer = new StringWriter())
        {
            wrapper.WriteTo(writer, HtmlEncoder.Default);
            return new HtmlString(writer.ToString());
        }
    }

    private static TagBuilder BuildDropdownList(string buttonId, IEnumerable<SelectListItem> selectListItems, string selectedValue)
    {
        var ul = new TagBuilder("ul")
        {
            Attributes =
            {
                { "class", "dropdown-menu" },
                { "aria-labelledby", buttonId }
            }
        };

        foreach (var item in selectListItems)
        {
            if (item.Disabled)
            {
                var disabledLi = new TagBuilder("li");
                disabledLi.InnerHtml.AppendHtml($"<span class=\"dropdown-item disabled\">{item.Text}</span>");
                ul.InnerHtml.AppendHtml(disabledLi);
                continue;
            }

            var li = new TagBuilder("li");
            var link = new TagBuilder("a")
            {
                Attributes =
                {
                    { "class", $"dropdown-item {item.Value == selectedValue ? "active" : ""}" },
                    { "data-value", item.Value },
                    { "href", "#" }
                }
            };
            link.InnerHtml.Append(item.Text);
            li.InnerHtml.AppendHtml(link);
            ul.InnerHtml.AppendHtml(li);
        }

        return ul;
    }
}

前端交互脚本(添加到Test.cshtml或布局页)

<script>
    document.addEventListener('DOMContentLoaded', function() {
        // 监听下拉项点击,更新隐藏输入框和按钮文本
        document.querySelectorAll('.dropdown-item[data-value]').forEach(item => {
            item.addEventListener('click', function(e) {
                e.preventDefault();
                var value = this.getAttribute('data-value');
                var text = this.textContent.trim();
                var dropdown = this.closest('.dropdown');
                
                // 更新绑定用的隐藏输入框值
                dropdown.querySelector('input[type="hidden"]').value = value;
                // 更新按钮显示文本
                dropdown.querySelector('.dropdown-toggle').textContent = text;
                // 切换选中项的active样式
                dropdown.querySelectorAll('.dropdown-item').forEach(i => i.classList.remove('active'));
                this.classList.add('active');
            });
        });
    });
</script>

修正说明

  • 新增的隐藏输入框是模型绑定的关键,它的name属性与模型字段完全一致,表单提交时会自动把值发送到后台
  • 下拉触发按钮仅负责打开菜单,不再承担提交值的功能
  • 下拉项改为<a>标签并添加点击事件,点击时同步更新隐藏输入框的值和按钮显示文本,同时标记选中状态
  • 修复了原代码中的所有语法错误

内容的提问来源于stack exchange,提问作者Eduwow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:10:15