如何通过AJAX为多个表单内的相同下拉框正确填充选项
问题场景
- 多个独立项目表单放置在Bootstrap手风琴(accordion)折叠组件中,每个表单包含一个经理选择下拉框
- 期望通过接口拉取全局经理列表,为所有下拉框统一填充选项
- 实际表现:仅第一个下拉框成功加载选项,其余下拉框无选项
现有代码
HTML(Razor视图)
@foreach (Project project in Model.Projects) { <div class="accordion-item"> <h2 class="accordion-header" id="@($"heading{project.Key}")"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" aria-expanded="false"> <b> @project.Number- </b>: @project.Name </button> </h2> <div class="accordion-body"> <form> <div class="mb-3 row"> <label for="manager" class="col-sm-1 col-form-label">Manager</label> <div class="col-sm-3"> <select class="form-control capManager" asp-for="@project.Manager"></select> </div> <label for="budgetYear" class="col-sm-2 col-form-label" style="text-align: right">Budget Year</label> <div class="col-sm-1"> <input type="text" class="form-control" asp-for="@project.BudgetYear"> </div> <label for="budgetQuarter" class="col-sm-1 col-form-label">Quarter</label> <div class="col-sm-1"> <input type="text" class="form-control" asp-for="@project.BudgetQuarter"> </div> <div class="col-sm-3"></div> </div> </form> </div> </div> }
Javascript
function populateManagerOptions() { //Populate Manager options $.ajax({ url: '/Lookup/GetProjManagerData/', type: 'GET', data: { 'reason': 'dropdown' }, dataType: 'json', success: function (json) { const collection = document.getElementsByClassName("capManager"); for (let i = 0; i < collection.length; i++) { $.each(json, function (i, value) { collection[i].append($('<option>').text(value.mgrname).attr('value', value.projmgr)); }); } } }); }
后端Controller
[HttpGet] public IActionResult GetProjManagerData(string reason) { try { IEnumerable<TblProjmgr> codes = _context.TblProjmgr.AsEnumerable(); if (reason == "dropdown") return Json(codes); else return Json(new { data = codes }); } catch (Exception ex) { _logger.LogError(ex, DateTime.Now + "- Error occured during GetCauseData()"); return Json(new { error = ex.Message.ToString() }); } }
问题根因
核心问题是两层循环的索引变量重名导致逻辑错乱:
- 外层
for循环用变量i作为下拉框集合的遍历索引 - 内层
$.each遍历经理列表时,回调函数的第一个参数也命名为i,根据作用域规则,内层的i会遮蔽外层的下拉框索引 - 内层执行
collection[i].append()时,实际取的是经理项的索引来定位下拉框,导致选项错位追加、索引越界,最终只有第一个下拉框能拿到选项,其余下拉框无法正常填充
次要问题:
getElementsByClassName返回的是动态DOM集合,遍历过程中如果DOM变动会影响遍历结果,稳定性不足- Razor循环生成的label
for属性和selectid重复,不符合HTML规范,影响无障碍访问
修复方案
1. 修复JS逻辑(必改)
重命名内层循环的索引变量避免冲突,同时优化DOM遍历方式,使用jQuery选择器获取静态元素集合,提前拼接选项字符串减少DOM操作,逻辑更稳定:
function populateManagerOptions() { $.ajax({ url: '/Lookup/GetProjManagerData/', type: 'GET', data: { reason: 'dropdown' }, dataType: 'json', success: function (managerList) { // 统一生成所有option字符串,减少重复DOM操作 const optionHtml = managerList.map(mgr => `<option value="${mgr.projmgr}">${mgr.mgrname}</option>` ).join(''); // 批量给所有经理下拉框追加选项 $('.capManager').append(optionHtml); } }); }
注意:请确保在DOM加载完成后再调用该函数,比如放在
$(function() { populateManagerOptions() })中,或者放在页面底部脚本位置。Bootstrap手风琴折叠状态下的元素依然存在于DOM中,不需要等面板展开再填充。
2. 优化HTML规范(可选,建议修改)
给循环生成的select和对应label添加唯一标识,补全手风琴组件缺失的目标绑定属性,避免id/for属性重复,保证组件功能正常:
@foreach (Project project in Model.Projects) { // 生成当前表单唯一前缀 var formId = $"projectForm_{project.Key}"; var collapseId = $"collapse{project.Key}"; <div class="accordion-item"> <h2 class="accordion-header" id="@($"heading{project.Key}")"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#@collapseId" aria-expanded="false" aria-controls="@collapseId"> <b> @project.Number- </b>: @project.Name </button> </h2> <div id="@collapseId" class="accordion-collapse collapse" aria-labelledby="@($"heading{project.Key}")"> <div class="accordion-body"> <form> <div class="mb-3 row"> <!-- 绑定唯一for/id --> <label for="@($"{formId}_manager")" class="col-sm-1 col-form-label">Manager</label> <div class="col-sm-3"> <select class="form-control capManager" id="@($"{formId}_manager")" asp-for="@project.Manager"></select> </div> <label for="@($"{formId}_budgetYear")" class="col-sm-2 col-form-label" style="text-align: right">Budget Year</label> <div class="col-sm-1"> <input type="text" class="form-control" id="@($"{formId}_budgetYear")" asp-for="@project.BudgetYear"> </div> <label for="@($"{formId}_budgetQuarter")" class="col-sm-1 col-form-label">Quarter</label> <div class="col-sm-1"> <input type="text" class="form-control" id="@($"{formId}_budgetQuarter")" asp-for="@project.BudgetQuarter"> </div> <div class="col-sm-3"></div> </div> </form> </div> </div> </div> }
3. 后端代码无需修改
现有Controller接口逻辑正常,可以正确返回经理列表数据。
内容的提问来源于stack exchange,提问作者Matthew Czajka
相关产品推荐
相关产品推荐

