
本文详解如何在 ASP.NET Core 6 中构建支持动态增删行的 HTML 表格表单,通过 JavaScript 实时重索引 input 的 name 属性(如 POMaterials[0].Amount),确保删除任意行后仍能完整、有序地绑定到控制器模型。
本文详解如何在 asp.net core 6 中构建支持动态增删行的 html 表单表格,通过 javascript 实时重索引 input 的 name 属性(如 pomaterials[0].amount),确保删除任意行后仍能完整、有序地绑定到控制器模型。
在 ASP.NET Core 中使用数组式模型绑定(如 POMaterials[0].Amount, POMaterials[1].Amount)时,索引必须连续且从 0 开始,否则模型绑定将跳过缺失索引后的所有项,导致数据丢失。你当前的 counter 变量仅用于新增行,但删除中间行后未更新已有 的 name 和 id,破坏了索引连续性——这是模型绑定失败的根本原因。
✅ 正确方案:删除后立即重索引所有行
推荐使用 jQuery + 语义化事件委托 方案(更健壮、易维护),替代原生循环绑定 onclick(易重复绑定、难以解耦):
<!-- 在页面底部引入 jQuery(若尚未引入) --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
// 删除按钮点击事件(委托到 tbody,支持动态添加的行)
$('#grid2 tbody').on('click', 'button#clearrow', function () {
$(this).closest('tr').remove();
reindexRows(); // 删除后立即重索引
});
// 重索引函数:按当前 DOM 行序重新生成 name/id
function reindexRows() {
$('#grid2 tbody tr').each(function (rowIndex) {
const $row = $(this);
// 每行 4 个输入框,按列顺序对应属性
$row.find('td:eq(0) input').attr({
'name': `POMaterials[${rowIndex}].ItemNumber`,
'id': `POMaterials[${rowIndex}].ItemNumber`
});
$row.find('td:eq(1) input').attr({
'name': `POMaterials[${rowIndex}].Description`,
'id': `POMaterials[${rowIndex}].Description`
});
$row.find('td:eq(2) input').attr({
'name': `POMaterials[${rowIndex}].Amount`,
'id': `POMaterials[${rowIndex}].Amount`
});
$row.find('td:eq(3) input').attr({
'name': `POMaterials[${rowIndex}].UnitCost`,
'id': `POMaterials[${rowIndex}].UnitCost`
});
});
}
// 新增行函数(优化:使用模板字符串 + 避免内联 HTML 拼接)
function addRow() {
const rowIndex = $('#grid2 tbody tr').length;
const newRow = `
<tr>
<td><input type="text" name="POMaterials[${rowIndex}].ItemNumber" id="POMaterials[${rowIndex}].ItemNumber" class="form-control" required></td>
<td><input type="text" name="POMaterials[${rowIndex}].Description" id="POMaterials[${rowIndex}].Description" class="form-control" required></td>
<td><input type="text" name="POMaterials[${rowIndex}].Amount" id="POMaterials[${rowIndex}].Amount" class="form-control andi" required></td>
<td><input type="text" name="POMaterials[${rowIndex}].UnitCost" id="POMaterials[${rowIndex}].UnitCost" class="form-control andi" required></td>
<td>
<button type="button" id="clearrow" class="btn btn-sm btn-outline-danger">
<span class="k-icon k-i-minus"></span>
</button>
</td>
</tr>`;
$('#grid2 tbody').append(newRow);
}
⚠️ 关键注意事项
- 不要依赖全局 counter 变量:它无法反映真实 DOM 行数,应始终通过 $('#grid2 tbody tr').length 动态计算当前索引。
- 避免重复事件绑定:原生 for 循环中为每个单元格 onclick 赋值会导致多次绑定;使用事件委托(.on())一次注册,一劳永逸。
- name 属性决定模型绑定:ASP.NET Core 模型绑定器只认 name 属性,id 仅用于前端定位,但建议同步更新以保持一致性。
-
后端模型需匹配:确保控制器接收参数类型为 IList
或 POMaterial[],且 POMaterial 类包含 ItemNumber、Description、Amount、UnitCost 四个 public 属性。
✅ 验证是否成功?
提交表单后,在控制器中设置断点检查:
[HttpPost]
public IActionResult Create(MyViewModel model)
{
// model.POMaterials.Count 应等于前端实际填写的行数
// 每项的属性值均非 null(若标记了 [Required])
return View(model);
}
只要 reindexRows() 在每次增/删后准确执行,即可彻底解决“删中间行后绑定失效”问题,实现真正鲁棒的动态表格表单。











