MVC 表单无法发布对象列表 [英] MVC Form not able to post List of objects

查看:28
本文介绍了MVC 表单无法发布对象列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个有问题的 MVC Asp.net 应用程序.本质上,我有一个包含表单的视图,其内容绑定到一个对象列表.在这个循环中,它加载 PartialView 和被循环的项目.现在一切正常,直到提交表单.当它被提交时,控制器被发送一个空的对象列表.下面的代码演示了这些问题.

父视图:

@model IEnumerable@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" })){<div>@foreach(模型中的 var planVM){@Html.Partial("_partialView", planVM)}

}

_partialView:

@model PlanCompareViewModel<div>@Html.HiddenFor(p => p.PlanID)@Html.HiddenFor(p => p.CurrentPlan)@Html.CheckBoxFor(p => p.ShouldCompare)<input type="submit" value="Compare"/>

这些是上面代码的类:

计划视图模型:

公共类 PlansCompareViewModel{公共 int PlanID { 获取;放;}公共计划 CurrentPlan { 获取;放;}公共布尔应该比较{得到;放;}公共计划CompareViewModel(计划计划){应该比较 = 假;PlanID = plan.PlanId;当前计划 = 计划;}公共计划比较视图模型(){//TODO: 完成成员初始化}公共静态 IEnumerableCreatePlansVM(IEnumerable 计划){返回plans.Select(p => new PlansCompareViewModel(p)).AsEnumerable();}}

控制器:

公共类 PlansController : MyBaseController{[HttpPost]公共 ActionResult 比较计划(IEnumerable 模型){//这里传入的模型为NULL}}

问题出在控制器动作上.据我所知,它应该发布一个可枚举的 PlanCompareViewModel 列表,但它为空.在检查正在发送的发布数据时,它正在发送正确的参数.如果我将IEnumerable"更改为FormCollection",它会包含正确的值.谁能看出为什么活页夹没有创建正确的对象?我可以使用 javascript 来解决这个问题,但这违背了目的!任何帮助将不胜感激!

解决方案

您的模型是 null 因为您向表单提供输入的方式意味着模型绑定器无法区分要素.现在,这段代码:

@foreach(模型中的 var planVM){@Html.Partial("_partialView", planVM)}

没有为这些项目提供任何类型的索引.所以它会重复生成这样的 HTML 输出:

<input type="hidden" name="yourmodelprefix.CurrentPlan"/><input type="checkbox" name="yourmodelprefix.ShouldCompare"/>

但是,由于您想要绑定到集合,您需要使用索引命名表单元素,例如:

<input type="hidden" name="yourmodelprefix[0].CurrentPlan"/><input type="checkbox" name="yourmodelprefix[0].ShouldCompare"/><input type="hidden" name="yourmodelprefix[1].PlanID"/><input type="hidden" name="yourmodelprefix[1].CurrentPlan"/><input type="checkbox" name="yourmodelprefix[1].ShouldCompare"/>

该索引使模型绑定器能够关联单独的数据片段,从而构建正确的模型.所以这就是我建议你修复它的方法.与其使用局部视图循环遍历您的集合,不如利用模板的强大功能.以下是您需要遵循的步骤:

  1. 在视图的当前文件夹中创建一个 EditorTemplates 文件夹(例如,如果您的视图是 HomeIndex.cshtml,则创建文件夹 HomeEditorTemplates>).
  2. 在该目录中使用与您的模型匹配的名称创建强类型视图.在您的情况下,这将是 PlanCompareViewModel.cshtml.

现在,您在局部视图中的所有内容都想放入该模板中:

@model PlanCompareViewModel<div>@Html.HiddenFor(p => p.PlanID)@Html.HiddenFor(p => p.CurrentPlan)@Html.CheckBoxFor(p => p.ShouldCompare)<input type="submit" value="Compare"/>

最后,您的父视图简化为:

@model IEnumerable@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" })){<div>@Html.EditorForModel()

}

DisplayTemplatesEditorTemplates 足够聪明,可以知道它们何时处理集合.这意味着它们会自动为您的表单元素生成正确的名称,包括索引,以便您可以正确地建模绑定到集合.

so I have an MVC Asp.net app that is having issues. Essentially, I have a View that contains a form, and its contents are bound to a list of objects. Within this loop, it loads PartialView's with the items being looped over. Now everything works up till the submittion of the form. When it gets submitted, the controller is sent a null list of objects. The code below demonstates the problems.

Parent View:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @foreach (var planVM in Model)
    {
        @Html.Partial("_partialView", planVM)
    }
</div>
}

_partialView:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

And these are the classes for the above code:

PlanViewModel:

public class PlansCompareViewModel
{

    public int PlanID { get; set; }
    public Plan CurrentPlan { get; set; }
    public bool ShouldCompare { get; set; }
    public PlansCompareViewModel(Plan plan)
    {
        ShouldCompare = false;
        PlanID = plan.PlanId;
        CurrentPlan = plan;
    }

    public PlansCompareViewModel()
    {
        // TODO: Complete member initialization
    }
    public static IEnumerable<PlansCompareViewModel> CreatePlansVM(IEnumerable<Plan> plans)
    {
        return plans.Select(p => new PlansCompareViewModel(p)).AsEnumerable();
    }
}

Controller:

public class PlansController : MyBaseController
{
    [HttpPost]
    public ActionResult ComparePlans(IEnumerable<PlanCompareViewModel> model)
    {
         //the model passed into here is NULL
    }
}

And the problem is in the controller action. As far as I am aware, it should be posting an enumerable list of PlanCompareViewModels, yet it is null. When in inspect the post data being sent, it is sending the correct params. And if I were to change 'IEnumerable' to 'FormCollection', it contains the correct values. Can anyone see why the binder is not creating the correct object? I can get around this using javascript, but that defeats the purpose! Any help would be greatly appreciated!

解决方案

Your model is null because the way you're supplying the inputs to your form means the model binder has no way to distinguish between the elements. Right now, this code:

@foreach (var planVM in Model)
{
    @Html.Partial("_partialView", planVM)
}

is not supplying any kind of index to those items. So it would repeatedly generate HTML output like this:

<input type="hidden" name="yourmodelprefix.PlanID" />
<input type="hidden" name="yourmodelprefix.CurrentPlan" />
<input type="checkbox" name="yourmodelprefix.ShouldCompare" />

However, as you're wanting to bind to a collection, you need your form elements to be named with an index, such as:

<input type="hidden" name="yourmodelprefix[0].PlanID" />
<input type="hidden" name="yourmodelprefix[0].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[0].ShouldCompare" />
<input type="hidden" name="yourmodelprefix[1].PlanID" />
<input type="hidden" name="yourmodelprefix[1].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[1].ShouldCompare" />

That index is what enables the model binder to associate the separate pieces of data, allowing it to construct the correct model. So here's what I'd suggest you do to fix it. Rather than looping over your collection, using a partial view, leverage the power of templates instead. Here's the steps you'd need to follow:

  1. Create an EditorTemplates folder inside your view's current folder (e.g. if your view is HomeIndex.cshtml, create the folder HomeEditorTemplates).
  2. Create a strongly-typed view in that directory with the name that matches your model. In your case that would be PlanCompareViewModel.cshtml.

Now, everything you have in your partial view wants to go in that template:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

Finally, your parent view is simplified to this:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @Html.EditorForModel()
</div>
}

DisplayTemplates and EditorTemplates are smart enough to know when they are handling collections. That means they will automatically generate the correct names, including indices, for your form elements so that you can correctly model bind to a collection.

这篇关于MVC 表单无法发布对象列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
C#/.NET最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆