如何在 ASP.NET Core 中视图组件的表单和控制器之间进行交互?

Ned*_*daM 4 javascript c# ajax asp.net-core asp.net-core-viewcomponent

我是 ASP.NET Core 网页设计的初学者。我编写了一个视图组件,该组件具有一个表单,其中包含一些与视图模型相关的输入。这些输入之一是文件输入(IFormFile数据类型)。

我想将此视图模型提交给控制器的操作(POST操作),检查模型的有效性,如果模型状态有效,则返回另一个视图组件,如果模型状态无效,则使用此视图模型保留在该视图组件上。

这是我的视图模型: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; }
}   
Run Code Online (Sandbox Code Playgroud)

我的视图组件(控制器):PricingComponent.cs

public class PricingComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
    {               
        return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
    }
}
Run Code Online (Sandbox Code Playgroud)

我的视图组件(视图):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>
Run Code Online (Sandbox Code Playgroud)

我的家庭控制器: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
    }
}
Run Code Online (Sandbox Code Playgroud)

A计划

上述方法是我的初步计划。

问题

如果我像上面一样使用提交输入标记(asp-actionasp-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>
Run Code Online (Sandbox Code Playgroud)

问题

在此代码中:

  1. 如果模型状态无效,警报会正确发送,但是
  2. 如果模型状态有效,则formFile输入不会正确发送到操作,并且在视图模型中为空。

我不知道我是否应该采用原始方法或替代方法来解决这些问题。你知道我哪里错了吗?

Ren*_*ena 5

不确定如何调用视图组件,以下是工作演示:

对于 A 计划

1.创建ViewComponents/PricingComponent.csViewComponents/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));
    }
}
Run Code Online (Sandbox Code Playgroud)

2.创建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>
Run Code Online (Sandbox Code Playgroud)

3.创建Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.

<h1>Excel....</h1>
Run Code Online (Sandbox Code Playgroud)

项目结构:

在此输入图像描述

4 Views/Home/Index.cshtml.:

@await Component.InvokeAsync("PricingComponent")
Run Code Online (Sandbox Code Playgroud)

5.家庭控制器:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

对于B计划

1.创建ViewComponents/PricingComponent.csViewComponents/ShowPricingExcelComponent.cs.

2.创建Views/Shared/Components/PricingComponent/PricingView.cshtml.

首先,应该是type="button"这样,否则会调用两次后端。其次,你在ajax中所做的不正确,更详细的解释你可以参考这个答案IsValidPricing。最后,你不能通过获取value的值来判断modelstate在你的 sucess 函数中。因为你获取的值始终是你第一次渲染页面的数据,所以当 ajax 回发时你无法获取更改后的 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>
Run Code Online (Sandbox Code Playgroud)

3.创建Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.

<h1>Excel....</h1>
Run Code Online (Sandbox Code Playgroud)

4 Views/Home/Index.cshtml.:

@await Component.InvokeAsync("PricingComponent")
<div id="ShowExcelTable"></div>
Run Code Online (Sandbox Code Playgroud)

5.家庭控制器:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述