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

.NET Core Razor点击按钮执行C#方法及无刷新更新表格方案

Hey, 我一眼就看出你当前的问题出在哪了——你写的@Model.RuleStepMoveUp()是Razor服务器端渲染代码,页面加载的时候服务器就会执行这个方法,根本等不到你点击按钮。这就是为啥页面一加载就触发了方法,还抛出空引用异常——那会儿子项的Model数据大概率还没初始化好呢。

下面给你一套完整的解决方案,既能实现点击按钮才执行后端方法,还能做到无刷新更新表格:

第一步:把后端方法改成Razor Pages规范的Post Handler

因为你用的是Razor Pages项目,最好遵循它的Handler命名规则,把移动逻辑改成POST请求的Handler,这样前端可以通过AJAX安全调用,还能避免GET请求的副作用。

比如在Index.cshtml.cs里调整方法:

// 上移操作的Post Handler,接收子项ID参数
public async Task<IActionResult> OnPostMoveUpAsync(int detailId)
{
    // 1. 找到要移动的子项
    var targetStep = await _context.RuleSteps.FindAsync(detailId);
    if (targetStep == null)
    {
        return NotFound("子项不存在");
    }

    // 2. 找到同主项的上一个子项(按排序值倒序找第一个比当前小的)
    var prevStep = await _context.RuleSteps
        .Where(s => s.RuleId == targetStep.RuleId && s.SortOrder < targetStep.SortOrder)
        .OrderByDescending(s => s.SortOrder)
        .FirstOrDefaultAsync();
    
    if (prevStep != null)
    {
        // 3. 交换两个子项的排序值
        (targetStep.SortOrder, prevStep.SortOrder) = (prevStep.SortOrder, targetStep.SortOrder);
        await _context.SaveChangesAsync();
    }

    // 4. 返回更新后的同主项子项列表,供前端更新表格
    var updatedSteps = await _context.RuleSteps
        .Where(s => s.RuleId == targetStep.RuleId)
        .OrderBy(s => s.SortOrder)
        .ToListAsync();
    
    return new JsonResult(updatedSteps);
}

// 下移操作同理
public async Task<IActionResult> OnPostMoveDownAsync(int detailId)
{
    var targetStep = await _context.RuleSteps.FindAsync(detailId);
    if (targetStep == null)
    {
        return NotFound("子项不存在");
    }

    var nextStep = await _context.RuleSteps
        .Where(s => s.RuleId == targetStep.RuleId && s.SortOrder > targetStep.SortOrder)
        .OrderBy(s => s.SortOrder)
        .FirstOrDefaultAsync();
    
    if (nextStep != null)
    {
        (targetStep.SortOrder, nextStep.SortOrder) = (nextStep.SortOrder, targetStep.SortOrder);
        await _context.SaveChangesAsync();
    }

    var updatedSteps = await _context.RuleSteps
        .Where(s => s.RuleId == targetStep.RuleId)
        .OrderBy(s => s.SortOrder)
        .ToListAsync();
    
    return new JsonResult(updatedSteps);
}

第二步:前端用AJAX触发方法,无刷新更新表格

先给子项表格加个ID,方便后续动态更新;然后给按钮绑定点击事件,用AJAX调用后端Handler,拿到更新后的数据再刷新表格内容:

1. 调整表格HTML结构

<!-- 给表格加ID,方便后续操作 -->
<table id="ruleStepsTable" class="table">
    <thead>
        <tr>
            <th>子项名称</th>
            <th>排序值</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var step in Model.CurrentRuleSteps)
        {
            <tr data-step-id="@step.Id">
                <td>@step.Name</td>
                <td>@step.SortOrder</td>
                <td>
                    <!-- 用data-id存储子项ID,方便JS获取 -->
                    <button class="btn btn-sm btn-primary btn-move-up" data-id="@step.Id">上移</button>
                    <button class="btn btn-sm btn-secondary btn-move-down" data-id="@step.Id">下移</button>
                </td>
            </tr>
        }
    </tbody>
</table>

<!-- 手动添加防CSRF令牌(如果页面没有form标签的话) -->
@Html.AntiForgeryToken()

2. 编写AJAX逻辑

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
    // 绑定上移按钮点击事件
    bindMoveEvents();

    // 封装绑定事件的函数,方便动态更新后重新绑定
    function bindMoveEvents() {
        $('.btn-move-up').off('click').click(function() {
            performMove($(this).data('id'), 'MoveUp');
        });

        $('.btn-move-down').off('click').click(function() {
            performMove($(this).data('id'), 'MoveDown');
        });
    }

    // 封装移动操作的AJAX逻辑
    function performMove(detailId, handlerName) {
        $.ajax({
            url: '/Index?handler=' + handlerName, // Razor Pages Handler路由格式
            type: 'POST',
            data: { detailId: detailId },
            headers: {
                // 必须携带防CSRF令牌,否则请求会被拒绝
                'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
            },
            success: function(updatedSteps) {
                // 成功后更新表格内容
                updateRuleStepsTable(updatedSteps);
                alert('操作成功!');
            },
            error: function(xhr) {
                alert('操作失败:' + xhr.responseText);
            }
        });
    }

    // 封装表格更新逻辑
    function updateRuleStepsTable(steps) {
        var tbody = $('#ruleStepsTable tbody');
        tbody.empty(); // 清空旧内容

        // 循环生成新的表格行
        $.each(steps, function(index, step) {
            var rowHtml = `<tr data-step-id="${step.Id}">
                            <td>${step.Name}</td>
                            <td>${step.SortOrder}</td>
                            <td>
                                <button class="btn btn-sm btn-primary btn-move-up" data-id="${step.Id}">上移</button>
                                <button class="btn btn-sm btn-secondary btn-move-down" data-id="${step.Id}">下移</button>
                            </td>
                        </tr>`;
            tbody.append(rowHtml);
        });

        // 重新绑定按钮事件(因为新行是动态生成的)
        bindMoveEvents();
    }
});
</script>

关键注意事项

  1. 防CSRF保护:Razor Pages默认开启了CSRF防护,所以所有POST请求必须携带__RequestVerificationToken,要么通过页面的form标签自动生成,要么手动添加@Html.AntiForgeryToken()。
  2. 动态元素事件绑定:因为表格行是动态更新的,新生成的按钮需要重新绑定点击事件,否则点击会无效,上面的代码用off('click').click()解决了这个问题。
  3. Handler命名规则:Razor Pages的Handler方法必须以OnPost或OnGet开头,前端路由用?handler=XXX(XXX是方法名去掉OnPost/OnGet后的部分,比如OnPostMoveUpAsync对应handler=MoveUp)。

这样改完之后,只有点击按钮时才会调用后端的C#方法,而且页面不用刷新就能实时更新子项表格的顺序,体验非常流畅。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:58:30