如何从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
相关产品推荐
相关产品推荐

