Razor MVC如何实现嵌套列表与HTML元素递归绑定编辑及回传修复
问题解决方法
核心原因
ASP.NET MVC默认模型绑定要求嵌套集合的表单元素name属性必须携带完整层级路径和索引,比如:
- 第一层子元素:
ListaMateriales[0].Material - 第二层子元素:
ListaMateriales[0].ListaMateriales[0].Cantidad
你当前递归用foreach生成的所有输入框name都为material.xxx,没有索引和层级路径,所以绑定器只能识别到第一个同name的元素。
改造方案
- 修改递归Helper的参数,新增前缀参数用于拼接层级路径,改用
for循环遍历集合获取索引 - 手动拼接或通过TemplateInfo设置字段前缀,保证所有表单元素的
name符合绑定规则
改造后的核心代码示例
@helper ShowTree(List<DulcesDeLaRosa.Models.BilletesViewModel> ListaMateriales, string prefix = "ListaMateriales", int nivel = 0) { if (ListaMateriales == null || !ListaMateriales.Any()) return; // 用for循环拿索引,不要用foreach for (int i = 0; i < ListaMateriales.Count; i++) { var material = ListaMateriales[i]; // 拼接当前层级的前缀 var currentPrefix = $"{prefix}[{i}]"; var marginLeft = 30 * nivel; var marginString = marginLeft + "px;"; <div class="form-inline col-12" style="margin-top:2%"> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) @* 设置当前上下文的字段前缀,后续EditorFor会自动拼接 *@ @{ ViewData.TemplateInfo.HtmlFieldPrefix = currentPrefix; } @Html.HiddenFor(m => material.Id) <hr /> <div class="col-4 delarosa-max-width"> <div class="form-group col-12 delarosa-max-width" style="margin-left:@marginString"> @Html.EditorFor(m => material.Material, new { htmlAttributes = new { @class = "form-control delarosa-max-width" } }) @Html.ValidationMessageFor(m => material.Material, "", new { @class = "text-danger" }) </div> </div> <div class="col-2"> <div class="form-group col-12 delarosa-max-width"> @Html.EditorFor(m => material.Cantidad, new { htmlAttributes = new { @class = "form-control delarosa-max-width" } }) @Html.ValidationMessageFor(m => material.Cantidad, "", new { @class = "text-danger" }) </div> </div> <div class="col-2"> <div class="form-group col-12 delarosa-max-width"> @Html.DropDownListFor(m => material.UnidadDeMedidaId, material.ListaUnidadesDeMedida, new { @class = "form-control delarosa-max-width" }) @Html.ValidationMessageFor(m => material.UnidadDeMedidaId, "", new { @class = "text-danger" }) </div> </div> <div class="col-2"> <div class="form-group col-12 delarosa-max-width"> @Html.EditorFor(m => material.Merma, new { htmlAttributes = new { @class = "form-control delarosa-max-width" } }) @Html.ValidationMessageFor(m => material.Merma, "", new { @class = "text-danger" }) </div> </div> <div class="col-2"> <div class="form-group col-12"> <div class="col-9 offset-1"> <button type="submit" id="BtnAgregar" name="btn" class="btn btn-info delarosa-background" value="Agregar|@material.Id">Agregar Hijo</button> </div> <div class="col-2"> <button type="submit" id="BtnEliminar" name="btn" class="btn btn-info delarosa-background" value="Eliminar|@material.Id">X</button> </div> </div> </div> </div> @* 递归调用子列表,传入拼接好的下一级前缀 *@ if (material.ListaMateriales != null && material.ListaMateriales.Any()) { <ul> @ShowTree(material.ListaMateriales, $"{currentPrefix}.ListaMateriales", nivel + 1) </ul> } @* 用完重置前缀,避免影响外层元素 *@ @{ ViewData.TemplateInfo.HtmlFieldPrefix = ""; } } }
- 调用Helper的地方去掉contador参数,改成:
@ShowTree(Model.ListaMateriales, "ListaMateriales", 0)
注意事项
- 不需要修改控制器代码,Action参数依旧直接接收
BilletesViewModel即可,模型绑定器会自动解析所有层级的嵌套列表 - 动态添加/删除节点时,要保证新增节点的
name属性索引是连续的,否则绑定器会跳过索引不连续的元素 - 如果遇到校验不生效的问题,检查ValidationMessageFor对应的
name是否和输入框的name完全一致
内容的提问来源于stack exchange,提问作者IvnBam
相关产品推荐
相关产品推荐

