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

ASP.NET Tag Helpers与Bootstrap下拉菜单结合问题排查

问题描述

我在实现文章创建功能时,来源选择部分出现错误,相关代码如下:

控制器代码

[HttpGet]
public async Task<IActionResult> Create()
{
        var sources = await _sourceServices.GetSourceNameAndId();

        var listSources = new List<SourceNameAndIdModel>();

        foreach (var source in sources)
        {
            listSources.Add(_mapper.Map<SourceNameAndIdModel>(source));
        }

        var viewModel = new ArticleCreateViewModel()
        {
            SourceNameAndIdModels = listSources
        };

        return View(viewModel);
}

[HttpPost]
public async Task<IActionResult> Create(ArticleCreateViewModel viewModel)
{
      await _articleService.CreateArticle(_mapper.Map<ArticleDTO>(viewModel));

      return RedirectToAction("Index", "Article");
}

相关DTO与模型

// SourceNameAndIdDTO
public class SourceNameAndIdDTO
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

// SourceNameAndIdModel
public class SourceNameAndIdModel
{
    public string Name { get; set; }
    public Guid Id { get; set; }
}

// ArticleCreateViewModel
public class ArticleCreateViewModel
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public string Title { get; set; }
    public string Description { get; set; }
    public string Body { get; set; }

    public Guid SourceId { get; set; }

    public DateTime CreationDate { get; set; }
    public List<SourceNameAndIdModel> SourceNameAndIdModels { get; set; }
}

视图代码

@model ArticleCreateViewModel

<div class="container">
    <h2>Edit article</h2>
    <div class="row gx-5">
        <form asp-controller="Article" asp-action="Create" asp-antiforgery="true" method="post">
            <div>
                <input type="hidden" asp-for="Id" />
                <div class="mb-3">
                    <label class="col-sm-2 col-form-label" asp-for="Title"></label>
                    <input class="form-control" type="text" asp-for="Title">
                </div>
                <div class="mb-3">
                    <label class="col-sm-2 col-form-label" asp-for="Description"></label>
                    <textarea class="form-control" asp-for="Description"></textarea>
                </div>
                <div class="mb-3">
                    <label class="col-sm-2 col-form-label" asp-for="Body"></label>
                    <textarea class="form-control" asp-for="Body"></textarea>
                </div>

                <div class="dropdown">
                    <a class="btn btn-secondary dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                        Source
                    </a>
                    <ul class="dropdown-menu">
                        @foreach (var item in @Model.SourceNameAndIdModels)
                        {
                            <li><select class="dropdown-item" asp-for="SourceId" asp-items="@item.Id">@item.Name</select></li>
                        }
                    </ul>
                </div>

                <div class="mb-3">
                    <label class="col-sm-2 col-form-label" asp-for="CreationDate"></label>
                    <input type="datetime-local" class="form-control" asp-for="CreationDate">
                </div>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
</div>

请问如何正确结合ASP.NET Tag Helpers与Bootstrap下拉菜单实现来源选择?


错误分析

视图中来源选择部分的核心问题:

  1. 错误生成多个<select>控件:循环里每个<li>都生成一个独立的<select>,每个控件只对应一个选项,这不符合表单选择逻辑,提交时无法正确绑定SourceId,同时视觉体验混乱。
  2. asp-items属性用法错误:该属性需要接收IEnumerable<SelectListItem>类型的集合,用于生成<option>列表,而不是传入单个Id值。

正确实现方案

方案一:Bootstrap样式原生Select控件(推荐)

这种方式完美适配ASP.NET模型绑定,同时Bootstrap提供了原生表单控件的样式支持,实现最简单。

步骤1:修改ViewModel或控制器逻辑

在ArticleCreateViewModel中添加用于生成选项的集合属性:

public IEnumerable<SelectListItem> SourceOptions { get; set; }

在控制器的Create GET方法中,直接将DTO转换为SelectListItem集合:

var viewModel = new ArticleCreateViewModel()
{
    SourceOptions = sources.Select(s => new SelectListItem
    {
        Text = s.Name,
        Value = s.Id.ToString()
    })
};

步骤2:替换视图中的下拉菜单代码

把原来的dropdown部分替换为以下代码:

<div class="mb-3">
    <label class="col-sm-2 col-form-label" asp-for="SourceId"></label>
    <select class="form-select" asp-for="SourceId" asp-items="@Model.SourceOptions">
        <option value="">请选择文章来源</option>
    </select>
</div>

这样ASP.NET Tag Helper会自动生成完整的<option>列表,Bootstrap的form-select类会提供下拉样式,提交表单时选中的Id会自动绑定到ViewModel.SourceId。

方案二:Bootstrap下拉按钮+JavaScript(自定义样式场景)

如果需要Bootstrap下拉按钮的样式,而非原生select样式,可以通过隐藏输入框结合JS实现绑定:

修改视图中的下拉菜单代码

<div class="mb-3">
    <label class="col-sm-2 col-form-label" asp-for="SourceId"></label>
    <!-- 隐藏输入框用于存储选中的SourceId -->
    <input type="hidden" asp-for="SourceId" id="sourceIdInput" />
    <div class="dropdown">
        <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
            <!-- 默认显示选中的来源或提示文本 -->
            @(Model.SourceId != Guid.Empty ? Model.SourceNameAndIdModels.FirstOrDefault(s => s.Id == Model.SourceId)?.Name : "请选择来源")
        </button>
        <ul class="dropdown-menu">
            @foreach (var item in Model.SourceNameAndIdModels)
            {
                <li><a class="dropdown-item" href="#" data-source-id="@item.Id">@item.Name</a></li>
            }
        </ul>
    </div>
</div>

<script>
    // 绑定下拉项点击事件,设置SourceId值并更新按钮文本
    document.querySelectorAll('.dropdown-item[data-source-id]').forEach(item => {
        item.addEventListener('click', function(e) {
            e.preventDefault();
            const sourceId = this.getAttribute('data-source-id');
            document.getElementById('sourceIdInput').value = sourceId;
            this.closest('.dropdown').querySelector('.dropdown-toggle').textContent = this.textContent;
        });
    });
</script>

点击下拉项时,JS会将选中的Id存入隐藏输入框,提交表单时即可正确绑定到ViewModel.SourceId,同时下拉按钮会显示选中的来源名称。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:15:43