类别中包含一个产品的集合属性,如何向数据库添加一条类别记录的同时,添加任意多个产品。
public class Product
{
[DisplayName("产品名称")]
public string Name { get; set; }
}
public class Category
{
[DisplayName("类别名称")]
public string Name { get; set; }
private IList<Product> _products = new List<Product>();
public IList<Product> Products
{
get { return _products; }
set { _products = value; }
}
}
□ 思路
控制器方法能接收的格式为:
Category.Name
Category.Products[0].Name
Category.Products[1].Name
...
前台视图使用jquery动态生成input,并把input的name属性设置成Category.Products[i].Name格式。
□ Home/Index.cshtml视图
@model AddingMultipleNestedData.Models.Category
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm("Create", "Home", FormMethod.Post, new {id = "addForm"}))
{
<div>
@Html.LabelFor(m => m.Name)
@*@Html.EditorFor(m => m.Name)*@
@Html.TextBox("Category.Name")
</div>
<div id="products"></div>
<div>
<input id="btnAddProduct" type="button" value="添加产品"/>
</div>
<div>
<input type="submit" value="提交"/>
</div>
}
@section scripts
{
<script type="text/javascript">
$(function() {
var noOfProducts = 0;
$('#btnAddProduct').click(function() {
var product = getNestedName("Category.Products", noOfProducts);
noOfProducts++;
$('#products').append("<input type='text' name='"+product+".Name' /><p>");
});
});
function getNestedName(itemName, itemNumber) {
return (itemName + "[" + itemNumber + "]");
}
</script>
}
没有添加产品前:
添加产品集合:
□ HomeController
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Create(Category category)
{
return View();
}
断点调试:
更新 2014.05.07
以上做法至少有二个弊端:
1、无法对新添加的集合属性对应的个体进行验证。
2、如果破坏集合元素的连续性,会影响控制器不能完全接收所有集合记录。
Category.Name
Category.Products[0].Name
Category.Products[3].Name
Category.Products[6].Name
控制器只能接收到集合中的第一条记录,即Category.Products[0].Name,也就是说,一旦集合元素不是连续的,控制器将不能接收到全部集合记录。
把noOfProducts++;改成noOfProducts = noOfProducts + 3;
在下一篇中,将寻找一种既能验证集合元素,控制器又能接收不连续集合元素的解决方案!
关于3楼刘慧心所提的问题
总体来说,问题处在:
<input type="hidden" name="CgdModel.Ubis.Index" value="indexA" />
<input type="text" name="CgdModel.Ubis[indexA].Fdt"...
<input type="text" name="CgdModel.Ubis[indexA].Memo...
每一组Ubis属性所对应的Model,都对应着value值不同的隐藏域,大体应该这样:
<input type="hidden" name="CgdModel.Ubis.Index" value="0" />
<input type="text" name="CgdModel.Ubis[0].Fdt"...
<input type="text" name="CgdModel.Ubis[0].Memo...
<input type="hidden" name="CgdModel.Ubis.Index" value="1" />
<input type="text" name="CgdModel.Ubis[1].Fdt"...
<input type="text" name="CgdModel.Ubis[1].Memo...
...
□ 假设您的Models是这样:
public class CgdModel
{
public string Id { get; set; }
public string Name { get; set; }
public IList<Ubis> Ubis { get; set; }
}
public class Ubis
{
public string Fdt { get; set; }
public string Memo { get; set; }
}
□ 假设CgdController是这样:
public ActionResult Create()
{
return View(new CgdModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CgdModel cgdModel)
{
if (!ModelState.IsValid)
{
return View(cgdModel);
}
return Content("ok");
}
□ CgdController/Create.cshtml大致是这样:
@model VariableCollection.Models.CgdModel
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<input type="submit" value="产生采购单" />
<fieldset>
<legend>采购单</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
<fieldset>
<legend>明细</legend>
<table>
@for (int i = 0; i < 3; i++)
{
<tr>
<td><input type="hidden" name="Ubis.Index" value="@i" /></td>
<td>Fdt @i<input type="text" name="Ubis[@i].Fdt" /></td>
<td>Memo @i<input type="text" name="Ubis[@i].Memo" /></td>
</tr>
}
</table>
</fieldset>
}