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

如何从Controller Action返回至原ViewComponent?

问题描述

我在页面中嵌入了一个ValidateOrder ViewComponent,组件内包含一个提交至Controller Action的表单。相关代码如下:

索引页面代码

<div class="row">
    <div class="col-lg-4 col-12">
        @await Component.InvokeAsync("ValidateOrder")
    </div>
</div>

ViewComponent 代码

public async Task<IViewComponentResult> InvokeAsync()
{
    return View();
}

组件视图代码

<form asp-antiforgery="true" asp-area="Customer" asp-controller="Order" asp-action="Validate" method="post" class="form-horizontal" role="form">
    <div class="row">
        <div class="col">
            <input asp-for="Number" class="form-control" />
            <small>
                <span asp-validation-for="Number" class="text-danger"></span>
            </small>
        </div>
    </div>

    <div class="row">
        <div class="col">
            <button type="submit" class="btn btn-style-1 btn-primary">Check</button>
        </div>
    </div>
</form>

Controller Action 代码

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Validate(ValidateOrderViewModel model)
{
    if (!TryValidateModel(model))
    {
        return BadRequest();
    }
    
    var result = await orderService.ValidateAsync(model.Number);
    return View(result); // 如何返回至原ViewComponent?
}

尝试过返回View、RedirectToAction、ViewComponent甚至Json,都没达到预期效果。难道只能重定向至页面并通过ViewData传递结果吗?


解决方案

方案1:AJAX异步提交+组件内容替换(推荐)

通过AJAX提交表单,让Action返回渲染后的ViewComponent内容,再用JS替换页面上的组件容器,实现无刷新更新。

步骤1:给容器和表单加标识

修改索引页面的组件容器,增加ID方便后续替换:

<div class="row">
    <div id="validateOrderComponent" class="col-lg-4 col-12">
        @await Component.InvokeAsync("ValidateOrder")
    </div>
</div>

给组件内的表单加ID:

<form id="validateOrderForm" asp-antiforgery="true" asp-area="Customer" asp-controller="Order" asp-action="Validate" method="post" class="form-horizontal" role="form">
    <!-- 原有表单内容不变 -->
</form>

步骤2:添加AJAX提交脚本

在页面底部或组件视图中添加JS:

document.getElementById('validateOrderForm').addEventListener('submit', async function(e) {
    e.preventDefault();
    
    const formData = new FormData(this);
    const response = await fetch(this.action, {
        method: this.method,
        body: formData,
        headers: {
            'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
        }
    });
    
    if (response.ok) {
        const html = await response.text();
        document.getElementById('validateOrderComponent').innerHTML = html;
    } else {
        const errorHtml = await response.text();
        document.getElementById('validateOrderComponent').innerHTML = errorHtml;
    }
});

步骤3:修改Action返回ViewComponent

让Action直接返回渲染后的组件,支持传递验证结果和错误状态:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Validate(ValidateOrderViewModel model)
{
    // 验证失败时,传递原模型和错误信息到组件
    if (!ModelState.IsValid)
    {
        return ViewComponent("ValidateOrder", new { model = model });
    }
    
    var result = await orderService.ValidateAsync(model.Number);
    // 传递验证结果到组件
    return ViewComponent("ValidateOrder", new { model = model, validationResult = result });
}

步骤4:更新ViewComponent接收参数

修改InvokeAsync方法,支持接收模型和验证结果:

public async Task<IViewComponentResult> InvokeAsync(ValidateOrderViewModel model = null, object validationResult = null)
{
    ViewData["ValidationResult"] = validationResult;
    return View(model ?? new ValidateOrderViewModel());
}

步骤5:组件视图添加结果展示

在组件视图中增加结果显示区域:

<!-- 原有表单内容 -->

@if (ViewData["ValidationResult"] != null)
{
    <div class="mt-3">
        @{
            var result = (ValidationResult)ViewData["ValidationResult"];
        }
        @if (result.IsValid)
        {
            <div class="alert alert-success">订单验证通过</div>
        }
        else
        {
            <div class="alert alert-danger">@result.ErrorMessage</div>
        }
    </div>
}

方案2:TempData传递结果+页面重定向

如果不想用AJAX,可通过TempData存储结果,重定向回原页面后让ViewComponent读取并展示。

步骤1:修改Controller Action

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Validate(ValidateOrderViewModel model)
{
    if (!ModelState.IsValid)
    {
        TempData["ModelErrors"] = ModelState;
        return RedirectToAction("Index"); // 重定向回原页面
    }
    
    var result = await orderService.ValidateAsync(model.Number);
    TempData["ValidationResult"] = result;
    return RedirectToAction("Index");
}

步骤2:ViewComponent读取TempData

public async Task<IViewComponentResult> InvokeAsync()
{
    var model = new ValidateOrderViewModel();
    
    // 读取并恢复ModelState错误
    if (TempData["ModelErrors"] is ModelStateDictionary errors)
    {
        foreach (var error in errors)
        {
            ModelState.AddModelError(error.Key, error.Value.Errors.First().ErrorMessage);
        }
    }
    
    // 读取验证结果
    ViewData["ValidationResult"] = TempData["ValidationResult"];
    
    return View(model);
}

步骤3:组件视图展示结果(同方案1的视图修改)


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 10:14:53