如何将复选框列表中的帖子数据绑定回自定义类

时间:2016-12-19 08:38:25

标签: c# asp.net-mvc asp.net-mvc-5.2

这个问题类似于之前提出的其他问题,但我的用法(和MVC知识)不同,所以我无法弄清楚如何调整其他答案以满足我的需求。

我有一个用户请求产品价格的表单。该产品有许多可选模块,这会影响整体价格。然后,控制器应该向我发送一封电子邮件,其中包含所选模块“DisplayName

我可以在请求表单中正确呈现这些模块标题,但在提交表单时无法读取它们。调试显示!ModelState.IsValid,并且在模型状态中存在转换异常:

  

从类型'System.String'到类型的参数转换   'MyNamspace.Models.MyProductModule'失败,因为没有类型转换器   可以在这些类型之间进行转换。

我的整个方法可能有误,但我已经在产品模型中定义了模块(SO的简化示例),工作于this tutorial

public class MyProductModule
{
    public string ModuleName { get; set; }
    public bool Checked { get; set; }
}

public class ProductRequest
{

    public ProductRequest()
    {
        Modules = LoadModules();
    }

    private List<MyProductModule> LoadModules()
    {
        return new List<MyProductModule>()
        {
            new MyProductModule() { ModuleName = "Module One", Checked = false },
            new MyProductModule() { ModuleName = "Module Two", Checked = false },
            new MyProductModule() { ModuleName = "Module Three", Checked = false }
        };
    }

    [DisplayName("MyProduct Modules")]
    public List<MyProductModule> Modules { get; set; }
}

这是我用来呈现复选框列表的代码:

@model MyNamespace.Models.ProductRequest

@foreach (var item in Model.Modules)
{
    <label>
        <input type="checkbox" name="Modules" value="@item.ModuleName" checked="@item.Checked" />
        @item.ModuleName
    </label>
}

以下是我尝试收集发布的表单数据的方法:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ProcessRequest(ProductRequest qd)
{
    if (!ModelState.IsValid)
    {
        return View("Index", qd); // Code exits here with ModelState error
    }
    else
    {
        StringBuilder sb = new StringBuilder();
        // null checks removed for brevity...
        sb.Append("<ol>");
        var selectedModules = qd.Modules.Where(x => x.Checked).Select(x => x).ToList();
        foreach (MyProductModule sm in selectedModules)
        {
            sb.AppendFormat("<li>{0}</li>", sm.ModuleName);
        }
        sb.Append("</ol>");
    }   
    // ....
}

非常感谢任何帮助或建议。作为一个长期的webforms开发人员,我发现MVC学习曲线无情!

解决方案

见帖子回答。

2 个答案:

答案 0 :(得分:2)

您可能希望像这样渲染视图:

for (var i = 0; i < Model.Modules.Count; i++ )
{
    var item = Model.Modules[i];
    <label>
        @Html.CheckBox(string.Format("Modules[{0}].Checked", i), item.Checked)
        @item.ModuleName
    </label>
    @Html.Hidden(string.Format("Modules[{0}].ModuleName", i), item.ModuleName)
}

答案 1 :(得分:2)

感谢Stephen Muecke ...

如建议的链接中所述,可以在不使用数据模型实例的情况下引用数据模型。因此,这是呈现复选框的正确方法:

<div>
    @for (int i = 0; i < Model.Modules.Count; i++)
    {   
        @Html.CheckBoxFor(m => m.Modules[i].Checked)
        @Html.LabelFor(m => m.Modules[i].Checked, Model.Modules[i].ModuleName)                  
        <br />
    }
</div>

这确保列出所有模块,即使用户实际上并未全部选择它们,验证也会失败。

然后在发布表单时自动完成模型绑定,因此用于处理表单的控制器代码不需要修改需要稍微修改。获取所有可用模块的代码应该是静态的。这样,可以方便地“读取”/使所有模块可用。然后将该静态列表与用户提交的列表(可能只包含部分模块或不包含任何可用模块)进行比较:

List<MyProductModule> allModules = ProductRequest.LoadModules();
foreach(MyProductModule sm in qd.Modules)
{
    if(sm.Checked)
    {
        sb.AppendFormat("<li>{0}</li>", allModules[qd.Modules.IndexOf(sm)].ModuleName);
    }
}