如何将复选框列表中的 post 数据绑定回自定义 class
How to bind post data from checkbox list back to custom class
这个问题与之前提出的其他问题类似,但我的用法(和 MVC 知识)非常不同,所以我不知道如何调整其他答案以满足我的需要。
我有一个表格,用户可以在其中请求产品价格。该产品有多个 可选 模块,这会影响整体价格。然后,控制器应向我发送一封电子邮件,其中包含所选模块' DisplayName
。
我可以在请求表单中渲染这些模块标题,但在提交表单时无法读回它们。调试显示!ModelState.IsValid
,model state内部出现转换异常:
The parameter conversion from type 'System.String' to type
'MyNamspace.Models.MyProductModule' failed because no type converter
can convert between these types.
我的整个方法可能是错误的,但我已经在产品模型中定义了模块(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>");
}
// ....
}
如有任何帮助或建议,我们将不胜感激。作为一名 long-term webforms 开发人员,我发现 MVC 学习曲线是无情的!
解决方案
查看发布的答案。
您可能希望像这样渲染视图:
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)
}
感谢 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>
这确保列出所有模块,即使用户实际上没有 select 所有模块,并且验证失败。
模型绑定会在表单发布时自动完成,因此处理表单的控制器代码不需要修改需要稍微修改。获取所有可用模块的代码应该是静态的。这样所有模块都可以 "read"/方便地使用。然后将该静态列表与用户提交的列表(可能只包含一些或 none 可用模块)进行比较:
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);
}
}
这个问题与之前提出的其他问题类似,但我的用法(和 MVC 知识)非常不同,所以我不知道如何调整其他答案以满足我的需要。
我有一个表格,用户可以在其中请求产品价格。该产品有多个 可选 模块,这会影响整体价格。然后,控制器应向我发送一封电子邮件,其中包含所选模块' DisplayName
。
我可以在请求表单中渲染这些模块标题,但在提交表单时无法读回它们。调试显示!ModelState.IsValid
,model state内部出现转换异常:
The parameter conversion from type 'System.String' to type 'MyNamspace.Models.MyProductModule' failed because no type converter can convert between these types.
我的整个方法可能是错误的,但我已经在产品模型中定义了模块(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>");
}
// ....
}
如有任何帮助或建议,我们将不胜感激。作为一名 long-term webforms 开发人员,我发现 MVC 学习曲线是无情的!
解决方案
查看发布的答案。
您可能希望像这样渲染视图:
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)
}
感谢 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>
这确保列出所有模块,即使用户实际上没有 select 所有模块,并且验证失败。
模型绑定会在表单发布时自动完成,因此处理表单的控制器代码不需要修改需要稍微修改。获取所有可用模块的代码应该是静态的。这样所有模块都可以 "read"/方便地使用。然后将该静态列表与用户提交的列表(可能只包含一些或 none 可用模块)进行比较:
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);
}
}