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

ASP.NET Core MVC动态增删对象列表提交异常问题求助

动态增删ASP.NET Core MVC列表项并正常提交的解决方案

问题描述

在ASP.NET Core MVC中实现动态添加/删除ProductFeature列表项,输入框命名遵循Product.ProductFeatures[0].TitleName的格式,但删除中间行后(比如3行删第2行),提交表单时仅能提交索引0的项,索引2的项被忽略,无法完成正常提交。

现有代码

模型类

public class ProductFeature
{
    public int Id { get; set; }
    public required string TitleName { get; set; }
    public required string Value { get; set; }
}

HTML结构

<div class="accordion-body w-50">
    <div id="features" class="form-group">
    </div>
    <input id="cmdAdd1" class="btn btn-primary" type="button" value="افزودن ویژگی" />
</div>

原JavaScript代码

var itemCount = 0;
const removedItems = [];

$('#cmdAdd1').click(function () { addNewFeature(); });

function addNewFeature() {
// Add new line with text field and button
    if (removedItems.length == 0) {
        var newItem = ' <div id="item' + itemCount + '" class="input-group mb-2">';
        newItem += '<input id="title' + itemCount + '" name="Product.ProductFeatures[' + itemCount + '].TitleName" placeholder="عنوان" class="form-control" type="text"/>';
        newItem += '<input id="value' + itemCount + '" name="Product.ProductFeatures[' + itemCount + '].Value" placeholder="مقدار" class="form-control" type="text"/>';
        newItem += '<input id="cmdAdd' + itemCount + '" type="button" class="btn btn-danger" value="حذف ویژگی"></input>';
        newItem += "</div>";

        $('#features').append(newItem);
        var thisItem = itemCount;
        $('#cmdAdd' + itemCount).click(function () { removeItem(thisItem); });
        itemCount++;
    }
    else {
        var id = removedItems[0];
        var newItem = ' <div id="item' + id + '" class="input-group mb-2">';
        newItem += '<input id="title' + id + '" name="Product.ProductFeatures[' + id + '].TitleName" placeholder="عنوان" class="form-control" type="text"/>';
        newItem += '<input id="value' + id + '" name="Product.ProductFeatures[' + id + '].Value" placeholder="مقدار" class="form-control" type="text"/>';
        newItem += '<input id="cmdAdd' + id + '" type="button" class="btn btn-danger" value="حذف ویژگی"></input>';
        newItem += "</div>";

        $('#features').append(newItem);
        thisItem = id;
        $('#cmdAdd' + id).click(function () { removeItem(thisItem); });
        removedItems.splice(0, 1);
    }
}

function removeItem(i) {
    $('#item' + i).remove();
    removedItems.push(i);
}

问题原因

ASP.NET Core的模型绑定规则要求列表项的索引是连续的非负整数。如果索引出现断层(比如删除中间项后剩下0和2),模型绑定器会停止处理后续项,导致后面的输入无法被绑定到模型中。你之前的代码复用了删除的索引,导致索引序列不连续,这就是提交时丢失项的根本原因。

修复方案

核心思路是:删除项后立即重新整理所有剩余项的索引,保证索引从0开始连续递增。同时去掉原有的复用索引逻辑,直接基于当前DOM元素数量生成新索引。

修改后的JavaScript代码

$(document).ready(function() {
    $('#cmdAdd1').click(function () { addNewFeature(); });
});

function addNewFeature() {
    // 获取当前已有的特征项数量,作为新项的索引
    const currentItemCount = $('#features .input-group').length;
    
    // 用模板字符串构建新项,简化代码
    const newItem = `
        <div class="input-group mb-2">
            <input name="Product.ProductFeatures[${currentItemCount}].TitleName" placeholder="عنوان" class="form-control" type="text"/>
            <input name="Product.ProductFeatures[${currentItemCount}].Value" placeholder="مقدار" class="form-control" type="text"/>
            <button type="button" class="btn btn-danger remove-btn">حذف ویژگی</button>
        </div>
    `;

    $('#features').append(newItem);
    
    // 给新增的删除按钮绑定事件
    $('#features .input-group:last .remove-btn').click(function() {
        // 删除当前行
        $(this).closest('.input-group').remove();
        // 重新整理所有项的索引
        reindexFeatureItems();
    });
}

function reindexFeatureItems() {
    // 遍历所有剩余的特征项,重新设置索引
    $('#features .input-group').each(function(index) {
        // 获取当前行的两个输入框
        const titleInput = $(this).find('input[name*=".TitleName"]');
        const valueInput = $(this).find('input[name*=".Value"]');
        
        // 更新name属性中的索引,保证连续
        titleInput.attr('name', `Product.ProductFeatures[${index}].TitleName`);
        valueInput.attr('name', `Product.ProductFeatures[${index}].Value`);
    });
}

修改说明

  • 移除了全局itemCount变量和removedItems数组,避免索引复用导致的断层问题
  • 使用类选择器.remove-btn绑定删除事件,无需依赖动态生成的ID,避免ID冲突
  • 删除项后立即调用reindexFeatureItems函数,重新为所有输入框设置连续索引
  • 用ES6模板字符串构建DOM元素,代码更易读维护

控制器接收注意事项

确保你的主模型(比如Product)包含ProductFeatures列表属性:

public class Product
{
    // 其他业务属性...
    public List<ProductFeature> ProductFeatures { get; set; } = new List<ProductFeature>();
}

在POST Action中直接接收模型即可:

[HttpPost]
public IActionResult SaveProduct(Product product)
{
    // product.ProductFeatures会包含所有提交的有效项
    // 后续验证、保存逻辑...
    return RedirectToAction("Index");
}

如果是编辑场景(已有带Id的ProductFeature),可以在每个行中添加隐藏的Id输入框,模型绑定会自动识别:

<input type="hidden" name="Product.ProductFeatures[${index}].Id" value="原有Id值"/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 00:04:52