【问题标题】:How to Interact between a view component's form and a controller in ASP.NET Core?如何在 ASP.NET Core 中的视图组件的表单和控制器之间进行交互?
【发布时间】:2021-04-23 10:11:37
【问题描述】:

我是使用 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; }
}   

我的视图组件(控制器):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>

问题

在这段代码中:

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

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

【问题讨论】:

  • 我已经检查了您的代码并且它正在使用验证。显示错误。为什么你使用return ViewComponent(...) 而不是返回View(...)?
  • @Justthebenno 您检查了哪个计划?您是如何编写有效并显示验证的代码的。我使用 return viewComponent(...) 因为我的视图是一个视图组件(在 ~/Views/shared/Components 中)。
  • 我做了一些改变,比如总是返回相同的 ViewComponent return ViewComponent("PricingComponent", new { pricing = pricing }); 我想避免在每种情况下都有新的组件。我构建了一个简单的 GET 方法,例如 ``` [HttpGet] public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { price = new PricingViewModel() });``` 并发送表单。结果:pasteboard.co/JKkcYTO.png
  • 对于您的用例,一个简单的视图似乎就足够了。 ViewComponents 更多用于advanced scenarios
  • @Justthebenno 我不会写我所有的项目。我只写其中的一部分。由于我的项目的其余部分,我必须使用视图组件,而不是简单的视图。

标签: javascript c# ajax asp.net-core asp.net-core-viewcomponent


【解决方案1】:

我无法重现您的错误。如所示,您的代码按预期工作。将显示一条验证消息。

为了使它成为一个工作示例,我首先添加了一个 GET 方法。

[HttpGet]
public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { pricing = new PricingViewModel() });

打开网址Home/ShowPricing

填写表格。

发送表格。并显示验证消息。

【讨论】:

  • 有一个错字,我猜是else if (!int.TryParse(updatePricing.colName, out temp)) 到else if (!int.TryParse(pricing.colName, out temp)),但是是的,你的代码。
  • 我能为你做的就是进行实时共享会话或类似的东西,我可以看看你的代码,我们可以尝试找出原因。
  • 是的。因为它返回的是 ViewComponent 而不是 View。
  • 有可能。但是,您的输入文件使它有点复杂。看看here 作为灵感。请记住,您需要将 colCode 等其他属性添加到表单数据 (formData.append("colCode", &lt;ValueOfColCode&gt;);)
【解决方案2】:

不知道如何调用视图组件,这里是工作演示:

对于 A 计划

1.创建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.创建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.创建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.创建ViewComponents/PricingComponent.cs和ViewComponents/ShowPricingExcelComponent.cs。

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

首先应该是type="button",否则会调用两次后端。其次,你在ajax中做的不正确,更详细的解释你可以参考this answer。最后,你无法判断modelstate 通过在您的 sucess 函数中获取IsValidPricing 值的值。因为您获取的值始终是您第一次渲染页面的数据,所以当 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>

3.创建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);
        }
    }
}

结果:

【讨论】:

  • 非常感谢您的帮助...我使用了计划 B (ajax) 并得到了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多