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

ASP.NET MVC动态添加级联下拉列表问题求助

解决动态添加级联下拉列表的重复ID问题

你猜的没错,问题核心就是所有动态生成的Team和Player下拉框都共用了同一个ID(TeamId和PlayerId),当你触发change事件时,JS只会找到页面上第一个匹配该ID的元素,所以新行的Player下拉始终跟着第一行走。下面是一套完整的解决方案,支持动态添加任意多行的二级级联下拉,同时保证模型能正确绑定提交:

第一步:调整视图模型为列表类型

首先你需要一个列表型的视图模型,因为要提交多行数据,单个模型无法接收:

public class PlayerSelectionViewModel
{
    public int TeamId { get; set; }
    public int PlayerId { get; set; }
}

// 用于页面的主模型
public class OCCreationViewModel
{
    public List<PlayerSelectionViewModel> PlayerSelections { get; set; }
    // 其他页面需要的属性...
}

第二步:修改主视图,用类代替ID绑定事件

把原来的单个下拉改成基于列表的渲染,给Team下拉加类team-dropdown,Player下拉加类player-dropdown,这样我们可以通过类来绑定事件,并且通过DOM关系找到当前行对应的Player下拉:

<form method="post">
    <a style="margin: 10px 0;" href="javascript:addRow();">Add Row</a>
    <table id="formTable">
        <thead>
            <tr>
                <th>Team</th>
                <th>Player</th>
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.PlayerSelections.Count; i++)
            {
                <tr>
                    <td>
                        @Html.DropDownListFor(m => m.PlayerSelections[i].TeamId, 
                            ViewBag.TeamList as SelectList, "-- Select Team--", 
                            new { @class = "form-control team-dropdown" })
                    </td>
                    <td>
                        @Html.DropDownListFor(m => m.PlayerSelections[i].PlayerId, 
                            new SelectList(Enumerable.Empty<SelectListItem>()), "--Select Player--", 
                            new { @class = "form-control player-dropdown" })
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <button type="submit">Submit</button>
</form>

第三步:修改JavaScript,用事件委托和DOM关联处理级联

原来的JS是绑定到固定ID,现在改成用事件委托(监听tbody的change事件,针对.team-dropdown),然后通过当前Team下拉的父行找到对应的.player-dropdown,这样不管新增多少行,事件都能生效:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script>
        $(document).ready(function () {
            // 事件委托:监听所有.team-dropdown的change事件,包括动态添加的
            $("#formTable tbody").on("change", ".team-dropdown", function () {
                var $teamDropdown = $(this);
                var teamId = $teamDropdown.val();
                // 找到当前行对应的player下拉
                var $playerDropdown = $teamDropdown.closest("tr").find(".player-dropdown");

                if (teamId) {
                    $.ajax({
                        type: "Post",
                        url: "/OCs/GetPlayerList",
                        data: { TeamId: teamId },
                        contentType: "html",
                        success: function (response) {
                            $playerDropdown.empty().append(response);
                        }
                    });
                } else {
                    // 如果没有选球队,清空player下拉
                    $playerDropdown.empty().append('<option value="">--Select Player--</option>');
                }
            });
        });

        function addRow() {
            var $tableBody = $("#formTable tbody");
            var rowCount = $tableBody.find("tr").length;
            // 复制第一行,修改name属性为新的索引值
            var $newRow = $tableBody.find("tr:first").clone();
            
            // 更新下拉框的name属性,让模型绑定能识别新的列表项
            $newRow.find(".team-dropdown").attr("name", `PlayerSelections[${rowCount}].TeamId`).val('');
            $newRow.find(".player-dropdown").attr("name", `PlayerSelections[${rowCount}].PlayerId`).val('');
            
            $tableBody.append($newRow);
        }
    </script>
}

第四步:调整控制器初始化和接收逻辑

修改Create方法,初始化列表至少有一行数据;同时添加接收表单的POST方法:

public ActionResult Create() {
    var model = new OCCreationViewModel
    {
        // 初始化至少一行空数据
        PlayerSelections = new List<PlayerSelectionViewModel> { new PlayerSelectionViewModel() }
    };
    ViewBag.TeamList = new SelectList(GetTeamList(), "IdTeam", "NameTeam");
    return View(model);
}

[HttpPost]
public ActionResult Create(OCCreationViewModel model) {
    if (ModelState.IsValid) {
        // 处理提交的多行数据,比如保存到数据库
        // model.PlayerSelections就是所有行的TeamId和PlayerId
        return RedirectToAction("Index");
    }
    // 如果验证失败,重新加载Team列表
    ViewBag.TeamList = new SelectList(GetTeamList(), "IdTeam", "NameTeam");
    return View(model);
}

// 你的GetTeamList和GetPlayerList方法可以保持不变
public List<Team> GetTeamList() {
    using (var db = new MyDbEntities()) {
        return db.Teams.ToList();
    }
}

public ActionResult GetPlayerList(int TeamId) {
    using (var db = new MyDbEntities()) {
        var playerList = db.Players.Where(x => x.IdRefTeam == TeamId).ToList();
        ViewBag.PlayerOptions = new SelectList(playerList, "IdPlayer", "PlayerNom");
        return PartialView("PlayerOptionsPartial");
    }
}

关键说明

  • 事件委托:用$("#formTable tbody").on("change", ".team-dropdown", ...)代替直接绑定到元素,这样动态添加的新行也能触发事件。
  • DOM关联:通过closest("tr").find(".player-dropdown")精准找到当前Team下拉对应的Player下拉,避免了ID冲突。
  • 模型绑定:动态修改下拉框的name属性为PlayerSelections[index].TeamId格式,确保提交时MVC能正确将多行数据绑定到列表模型中。

这样修改后,不管你添加多少行,每一行的Team下拉变化都会触发对应行的Player下拉更新,不会再出现和第一行联动的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:49:56