ASP.NET Core 中如何在视图组件的表单和控制器之间进行交互?
How to Interact between a view component's form and a controller in ASP.NET Core?
我是 ASP.NET Core 网页设计的初学者。我写了一个视图组件,它有一个表单,其中包含一些与视图模型相关的输入。这些输入之一是文件输入(IFormFile
数据类型)。
我想将此视图模型提交给控制器的操作(POST
操作),检查模型的有效性,如果模型状态有效,return 另一个视图组件,并保留如果模型状态无效,则在此视图组件上使用此视图模型。
这是我的视图模型:PricingViewModel.cs
public class PricingViewModel
{
[Display(Name = "Select a file")]
public IFormFile formFile { get; set; }
[Display(Name = "ColumnCode")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colCode { get; set; }
[Display(Name = "ColumnName")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colName { get; set; }
}
我的视图组件(控制器):PricingComponent.cs
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
我的视图组件(视图):PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
我的家庭控制器:HomeController.cs
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); // 1
}
else if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); //2
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return ViewComponent("PricingComponent", new { pricing = pricing }); //3
}
}
计划A
以上方法是我的主要方案。
问题
如果我像上面那样使用提交输入标签选项(asp-action
、asp-controller
),视图模型会正确发送,但我不知道如何处理模型的有效性和保留在此视图组件上。在上面的代码中,当 ShowPricing
动作运行时,如果模型状态有效,代码工作正常,但当模型无效(1,2,3)时,PricingView
不显示验证总结,并仅加载当前视图模型。
B计划
我使用 AJAX 将 viewModel
发送到操作,而不是显示验证摘要,我使用 AJAX 向用户发送警报。我将 PricingView
更改如下:
我的视图组件(视图):PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="fromFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '@Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
$.ajax({
type: "POST",
url: _url,
data: {
formFile: input,
colCode: $("#colCode").val(),
colName: $("#colName").val(),
},
success: function (result)
{
var IsValid = $('body').find('[name="IsValidPricing"]').val();
if (IsValid)
{
$("#ShowExcelTable").html(result);
}
else {
alert("Invalid Data");
}
},
});
});
});
</script>
<div class="form-group mt-4">
<input type="submit" value="Show" id="ShowPricingBtn" />
</div>
</form>
问题
在此代码中:
- 如果模型状态无效,警报会正确发送,但是
- 如果模型状态有效,
formFile
输入不会正确发送到操作,它在视图模型中为空。
我不知道我应该使用原始方法还是替代方法来解决这些问题。你知道我错在哪里吗?
我无法重现您的错误。如前所述,您的代码按预期工作。显示验证消息。
为了让它成为一个工作示例,我首先添加了一个 GET 方法。
[HttpGet]
public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { pricing = new PricingViewModel() });
打开URLHome/ShowPricing
填写表格。
发送表格。并显示验证消息。
不确定如何调用视图组件,这里是工作演示:
计划A
1.Create ViewComponents/PricingComponent.cs
和 ViewComponents/ShowPricingExcelComponent.cs
.
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
public class ShowPricingExcelComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("ShowPricingExcel", pricing));
}
}
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml
.
@model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml
.
<h1>Excel....</h1>
项目结构:
4.Views/Home/Index.cshtml
:
@await Component.InvokeAsync("PricingComponent")
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return View("Index", pricing);
}
if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return View("Index", pricing);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return View("Index", pricing); //3
}
}
}
结果:
对于计划 B
1.Create ViewComponents/PricingComponent.cs
和 ViewComponents/ShowPricingExcelComponent.cs
.
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml
.
首先应该是type="button"
,否则会调用两次backend.Secondly,你在ajax中做的不正确,更详细的解释可以参考。最后,你无法通过获取IsValidPricing
值的值来判断modelstate,你成功了function.Because你获取的值始终是你第一次渲染页面的数据,你无法获取ajax post 返回时更改的 ViewBag 值。
@model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
@*it should be type="button"*@
<input type="button" value="Show" id="ShowPricingBtn" />
</div>
</form>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '@Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
var fdata = new FormData();
fdata.append("formFile", input);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
type: "POST",
url: _url,
data: fdata,
contentType: false,
processData: false,
success: function (result)
{
console.log(result);
if (result==false)
{
alert("Invalid Data");
}
else {
$("#ShowExcelTable").html(result);
}
},
});
});
});
</script>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml
.
<h1>Excel....</h1>
4.Views/Home/Index.cshtml
:
@await Component.InvokeAsync("PricingComponent")
<div id="ShowExcelTable"></div>
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp)|| !int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
return Json(false);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return Json(false);
}
}
}
结果:
我是 ASP.NET Core 网页设计的初学者。我写了一个视图组件,它有一个表单,其中包含一些与视图模型相关的输入。这些输入之一是文件输入(IFormFile
数据类型)。
我想将此视图模型提交给控制器的操作(POST
操作),检查模型的有效性,如果模型状态有效,return 另一个视图组件,并保留如果模型状态无效,则在此视图组件上使用此视图模型。
这是我的视图模型:PricingViewModel.cs
public class PricingViewModel
{
[Display(Name = "Select a file")]
public IFormFile formFile { get; set; }
[Display(Name = "ColumnCode")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colCode { get; set; }
[Display(Name = "ColumnName")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colName { get; set; }
}
我的视图组件(控制器):PricingComponent.cs
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
我的视图组件(视图):PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
我的家庭控制器:HomeController.cs
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); // 1
}
else if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); //2
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return ViewComponent("PricingComponent", new { pricing = pricing }); //3
}
}
计划A
以上方法是我的主要方案。
问题
如果我像上面那样使用提交输入标签选项(asp-action
、asp-controller
),视图模型会正确发送,但我不知道如何处理模型的有效性和保留在此视图组件上。在上面的代码中,当 ShowPricing
动作运行时,如果模型状态有效,代码工作正常,但当模型无效(1,2,3)时,PricingView
不显示验证总结,并仅加载当前视图模型。
B计划
我使用 AJAX 将 viewModel
发送到操作,而不是显示验证摘要,我使用 AJAX 向用户发送警报。我将 PricingView
更改如下:
我的视图组件(视图):PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="fromFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '@Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
$.ajax({
type: "POST",
url: _url,
data: {
formFile: input,
colCode: $("#colCode").val(),
colName: $("#colName").val(),
},
success: function (result)
{
var IsValid = $('body').find('[name="IsValidPricing"]').val();
if (IsValid)
{
$("#ShowExcelTable").html(result);
}
else {
alert("Invalid Data");
}
},
});
});
});
</script>
<div class="form-group mt-4">
<input type="submit" value="Show" id="ShowPricingBtn" />
</div>
</form>
问题
在此代码中:
- 如果模型状态无效,警报会正确发送,但是
- 如果模型状态有效,
formFile
输入不会正确发送到操作,它在视图模型中为空。
我不知道我应该使用原始方法还是替代方法来解决这些问题。你知道我错在哪里吗?
我无法重现您的错误。如前所述,您的代码按预期工作。显示验证消息。
为了让它成为一个工作示例,我首先添加了一个 GET 方法。
[HttpGet]
public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { pricing = new PricingViewModel() });
打开URLHome/ShowPricing
填写表格。
发送表格。并显示验证消息。
不确定如何调用视图组件,这里是工作演示:
计划A
1.Create ViewComponents/PricingComponent.cs
和 ViewComponents/ShowPricingExcelComponent.cs
.
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
public class ShowPricingExcelComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("ShowPricingExcel", pricing));
}
}
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml
.
@model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml
.
<h1>Excel....</h1>
项目结构:
4.Views/Home/Index.cshtml
:
@await Component.InvokeAsync("PricingComponent")
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return View("Index", pricing);
}
if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return View("Index", pricing);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return View("Index", pricing); //3
}
}
}
结果:
对于计划 B
1.Create ViewComponents/PricingComponent.cs
和 ViewComponents/ShowPricingExcelComponent.cs
.
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml
.
首先应该是type="button"
,否则会调用两次backend.Secondly,你在ajax中做的不正确,更详细的解释可以参考IsValidPricing
值的值来判断modelstate,你成功了function.Because你获取的值始终是你第一次渲染页面的数据,你无法获取ajax post 返回时更改的 ViewBag 值。
@model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="@ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
@*it should be type="button"*@
<input type="button" value="Show" id="ShowPricingBtn" />
</div>
</form>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '@Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
var fdata = new FormData();
fdata.append("formFile", input);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
type: "POST",
url: _url,
data: fdata,
contentType: false,
processData: false,
success: function (result)
{
console.log(result);
if (result==false)
{
alert("Invalid Data");
}
else {
$("#ShowExcelTable").html(result);
}
},
});
});
});
</script>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml
.
<h1>Excel....</h1>
4.Views/Home/Index.cshtml
:
@await Component.InvokeAsync("PricingComponent")
<div id="ShowExcelTable"></div>
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp)|| !int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
return Json(false);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return Json(false);
}
}
}
结果: