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

ASP.NET Core使用jQuery Repeater时模型绑定失败问题咨询

问题原因

jQuery Repeater默认生成的表单字段name格式为前缀[索引][字段名],不符合ASP.NET Core模型绑定要求的前缀[索引].字段名规则。手动修改初始行的name属性后,插件内置的新增行命名生成逻辑未同步调整,因此新行name会生成错误的前缀[索引].0格式,导致绑定失败。

方案1:调整jQuery Repeater配置适配绑定规则

无需手动修改静态HTML的name属性,直接在插件初始化阶段重写命名生成逻辑即可:

  • 初始化时将repeater的prefix设置为C#模型中对应的集合属性名ProductDetails
  • 重写字段名生成逻辑,将插件默认生成的方括号嵌套字段名替换为点分隔格式
  • 新增/删除行后自动重排索引,保证索引从0开始连续,避免ASP.NET Core因索引断层跳过绑定
$('.repeater-wrapper').repeater({
  isFirstItemUndeletable: false,
  defaultValues: {
    Quantity: 1,
    Rate: 0
  },
  show: function () {
    $(this).slideDown();
    // 修正新增行的name格式
    updateInputNames(this);
  },
  hide: function (deleteEl) {
    if (confirm('确认删除当前商品行?')) {
      $(this).slideUp(deleteEl);
      // 删除后重排所有行索引
      reindexAllRows();
    }
  },
  ready: function () {
    // 初始化时修正首行name格式
    $('.repeater-wrapper .product-row').each(function () {
      updateInputNames(this);
    })
  }
});

// 修正单行内所有输入框的name格式
function updateInputNames(rowEl) {
  $(rowEl).find(':input').each(function () {
    const name = $(this).attr('name');
    // 匹配 ProductDetails[x][field] 格式
    const matchRes = name.match(/^ProductDetails\[(\d+)\]\[(\w+)\]$/);
    if (matchRes) {
      const idx = matchRes[1];
      const field = matchRes[2];
      $(this).attr('name', `ProductDetails[${idx}].${field}`);
    }
  });
}

// 重排所有行索引,保证连续
function reindexAllRows() {
  $('.repeater-wrapper .product-row').each(function (idx) {
    $(this).find(':input').each(function () {
      const newName = $(this).attr('name').replace(/ProductDetails\[\d+\]/, `ProductDetails[${idx}]`);
      $(this).attr('name', newName);
    });
  });
}

配置完成后,所有初始行、新增行的name都会自动生成为ProductDetails[0].ProductId这类符合绑定规则的格式,无需手动修改静态代码。

方案2:原生JS实现(推荐,无兼容问题)

jQuery Repeater长期无更新,适配框架规则成本高,可直接用几十行原生JS实现相同功能,完全适配ASP.NET Core绑定规则,无第三方依赖问题:

  1. 编写动态行容器与首行模板,首行name严格匹配模型绑定格式:
<div class="repeater-wrapper">
  <div id="product-container">
    <!-- 首行模板 -->
    <div class="product-row mb-2">
      <input class="form-control d-inline-block w-25" name="ProductDetails[0].ProductId" placeholder="商品ID" />
      <input type="number" class="form-control d-inline-block w-25" name="ProductDetails[0].Quantity" placeholder="数量" />
      <input type="number" class="form-control d-inline-block w-25" name="ProductDetails[0].Rate" placeholder="单价" />
      <button type="button" class="btn btn-sm btn-danger delete-row">删除</button>
    </div>
  </div>
  <button type="button" id="add-product" class="btn btn-sm btn-primary mt-2">新增商品行</button>
</div>
  1. 编写新增/删除/重排索引的JS逻辑:
// 新增行
$('#add-product').click(function () {
  const container = $('#product-container');
  const newIdx = container.find('.product-row').length;
  // 克隆首行,清空值,替换索引
  const newRow = container.find('.product-row').first().clone();
  newRow.find('input').val('');
  newRow.find(':input').each(function () {
    const name = $(this).attr('name').replace(/\[\d+\]/, `[${newIdx}]`);
    $(this).attr('name', name);
  });
  // 绑定删除事件
  newRow.find('.delete-row').click(deleteRowHandler);
  container.append(newRow);
  // 如果用了客户端不侵入验证,新增行后重新解析验证规则
  $.validator.unobtrusive.parse(newRow);
});

// 删除行逻辑
function deleteRowHandler() {
  $(this).closest('.product-row').remove();
  reindexRows();
}

// 重排索引保证连续
function reindexRows() {
  $('#product-container .product-row').each(function (idx) {
    $(this).find(':input').each(function () {
      const newName = $(this).attr('name').replace(/\[\d+\]/, `[${idx}]`);
      $(this).attr('name', newName);
    });
  });
}

// 初始绑定首行删除事件
$('.delete-row').click(deleteRowHandler);
注意事项
  • ASP.NET Core对集合类型的模型绑定要求索引从0开始且连续,删除行后必须重排索引,否则会出现断层后字段绑定丢失的问题
  • 如果使用客户端验证,动态新增的DOM节点需要手动调用$.validator.unobtrusive.parse()触发验证规则解析,否则新增字段不会触发客户端校验
  • 后端Action代码无需任何调整,前端name格式符合规则时,List<ProductDetails>参数会自动完成绑定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:56:50