如何用JavaScript新增表单表格行并提交至ASP.NET Core控制器
实现动态新增子表单行并提交到ASP.NET Core控制器
我之前处理过类似的场景,核心是要让ASP.NET Core的模型绑定能正确识别你新增的子项,关键在于遵循集合类型的命名规则。下面一步步给你讲具体实现:
1. 调整ViewModel和初始视图结构
首先确保你的IssueViewModel里的子项集合用List<T>(模型绑定对List支持更好),比如:
public class IssueViewModel { // 父项属性 public string Title { get; set; } // 子项集合,初始化避免空引用 public List<IssueItemViewModel> ChildItems { get; set; } = new List<IssueItemViewModel>(); } public class IssueItemViewModel { public string Description { get; set; } public int Quantity { get; set; } }
然后修改视图,用for循环生成初始子行(不要用foreach,因为foreach不会生成带索引的name属性):
@model IssueViewModel @section css { <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" /> } <h2>New Issue</h2> <form asp-action="Create" method="post"> <!-- 父项表单字段 --> <div class="form-group"> <label asp-for="Title"></label> <input asp-for="Title" class="form-control" /> <span asp-validation-for="Title" class="text-danger"></span> </div> <!-- 子项表格 --> <h4>Issue Items</h4> <table id="childItemsTable" class="table"> <thead> <tr> <th>Description</th> <th>Quantity</th> <th>Actions</th> </tr> </thead> <tbody> @for (int i = 0; i < Model.ChildItems.Count; i++) { <tr class="child-item-row"> <td> <input asp-for="ChildItems[i].Description" class="form-control" /> <span asp-validation-for="ChildItems[i].Description" class="text-danger"></span> </td> <td> <input asp-for="ChildItems[i].Quantity" type="number" class="form-control" /> <span asp-validation-for="ChildItems[i].Quantity" class="text-danger"></span> </td> <td> @if (i > 0) { <button type="button" class="btn btn-danger btn-remove-row">Remove</button> } </td> </tr> } </tbody> </table> <button type="button" id="btnAddRow" class="btn btn-primary">Add New Item</button> <button type="submit" class="btn btn-success">Save</button> </form>
2. 编写JavaScript实现动态增删行
用jQuery来处理行的克隆、索引更新和移除逻辑,关键是要更新所有表单元素的name和id属性里的索引值,确保符合ChildItems[index].PropertyName的格式:
@section scripts { <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { // 添加新行 $('#btnAddRow').click(function() { const tableBody = $('#childItemsTable tbody'); const rowCount = tableBody.find('.child-item-row').length; const lastRow = tableBody.find('.child-item-row:last'); // 克隆最后一行并清空输入值 const newRow = lastRow.clone(); newRow.find(':input').each(function() { // 替换name和id里的索引 const newName = $(this).attr('name').replace(/\[(\d+)\]/, `[${rowCount}]`); const newId = $(this).attr('id').replace(/\_(\d+)\_/, `_${rowCount}_`); $(this).attr('name', newName).attr('id', newId).val(''); // 清除旧的验证提示 $(this).next('.text-danger').text(''); }); // 给新行添加移除按钮 newRow.find('.btn-remove-row').remove(); newRow.find('td:last').html('<button type="button" class="btn btn-danger btn-remove-row">Remove</button>'); tableBody.append(newRow); }); // 移除行(用事件委托处理动态生成的按钮) $('#childItemsTable').on('click', '.btn-remove-row', function() { $(this).closest('.child-item-row').remove(); // 移除后重新更新所有行的索引,避免模型绑定跳过项 $('#childItemsTable tbody .child-item-row').each(function(index) { $(this).find(':input').each(function() { const updatedName = $(this).attr('name').replace(/\[(\d+)\]/, `[${index}]`); const updatedId = $(this).attr('id').replace(/\_(\d+)\_/, `_${index}_`); $(this).attr('name', updatedName).attr('id', updatedId); }); }); }); }); </script> }
3. 控制器动作处理提交
确保你的Create动作接收IssueViewModel,并处理模型验证和数据库保存:
[HttpPost] public IActionResult Create(IssueViewModel model) { if (ModelState.IsValid) { // 将ViewModel转换为实体并保存到数据库 var issue = new Issue { Title = model.Title, IssueItems = model.ChildItems.Select(item => new IssueItem { Description = item.Description, Quantity = item.Quantity }).ToList() }; _context.Issues.Add(issue); _context.SaveChanges(); return RedirectToAction(nameof(Index)); } // 验证失败时,确保子项集合不为空,返回视图时显示至少一行 if (model.ChildItems == null || model.ChildItems.Count == 0) { model.ChildItems = new List<IssueItemViewModel> { new IssueItemViewModel() }; } return View(model); }
关键注意点
- 模型绑定命名规则:必须保证子项表单元素的
name属性是ChildItems[index].PropertyName格式,ASP.NET Core才能将其识别为集合的一部分。 - 索引连续:移除行后必须重新排序索引,否则中间有空缺的索引会导致模型绑定跳过后面的子项。
- 验证处理:克隆行时要清除旧的验证提示,避免错误信息残留。
内容的提问来源于stack exchange,提问作者ifinlay
相关产品推荐
相关产品推荐

