您正在使用Ajax.BeginForm
并使用 jQuery 再次对表单进行 ajax 处理。那没有必要。但代码的真正问题是部分中输入字段的名称。你不尊重naming convention http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx由默认模型绑定器用于绑定到列表。
让我们举一个完整的示例(为了简单起见,删除所有噪音,例如实体框架):
Model:
public class Setting
{
public int SettingId { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
控制器:
public class SettingsController : Controller
{
public ActionResult Index()
{
// No idea why you are using ViewBag instead of view model
// but I am really sick of repeating this so will leave it just that way
ViewBag.Settings = Enumerable.Range(1, 5).Select(x => new Setting
{
SettingId = x,
Name = "setting " + x,
Value = "value " + x
}).ToList();
return View();
}
[HttpPost]
public ActionResult Edit(IList<Setting> setting)
{
// Currently for testing purposes only. Breakpoint is set to setting.Count
return Content(setting.Count.ToString());
}
}
View (~/Views/Settings/Index.cshtml
):
@using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
{
foreach (Setting item in ViewBag.Settings)
{
@Html.Partial("_SingleSetting", item)
}
<input type="submit" value="modify" />
}
@section scripts {
<script type="text/javascript">
$('#editSettings').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
alert(result);
}
});
}
return false;
});
</script>
}
设置部分(~/Views/Settings/_SingleSetting.cshtml
):
@model Setting
@{
var index = Guid.NewGuid().ToString();
ViewData.TemplateInfo.HtmlFieldPrefix = "[" + index + "]";
}
<input type="hidden" name="index" value="@index" />
<div class="control-group">
<label class="control-label">@Html.LabelFor(x => x.Name)</label>
<div class="controls">
@Html.EditorFor(model => model.Value)
</div>
</div>
请注意在部分内部如何需要更改 HtmlFieldPrefix 以便 html 帮助程序为您的输入字段生成正确的名称并遵守命名约定。
好吧,现在我们来剪掉ViewCrap并正确地做事(当然,即使用视图模型)。
一如既往,我们从编写视图模型开始:
public class MyViewModel
{
public IList<Setting> Settings { get; set; }
}
然后我们调整控制器:
public class SettingsController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
// you will probably wanna call your database here to
// retrieve those values, but for the purpose of my example that
// should be fine
model.Settings = Enumerable.Range(1, 5).Select(x => new Setting
{
SettingId = x,
Name = "setting " + x,
Value = "value " + x
}).ToList();
return View(model);
}
[HttpPost]
public ActionResult Edit(IList<Setting> setting)
{
// Currently for testing purposes only. Breakpoint is set to setting.Count
return Content(setting.Count.ToString());
}
}
View (~/Views/Settings/Index.cshtml
):
@model MyViewModel
@using (Html.BeginForm("Edit", "Settings", FormMethod.Post, new { @class = "form-horizontal", id = "editSettings" }))
{
@Html.EditorFor(x => x.Settings)
<input type="submit" value="modify" />
}
@section scripts {
<script type="text/javascript">
$('#editSettings').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
alert(result);
}
});
}
return false;
});
</script>
}
设置模型的编辑器模板(~/Views/Settings/EditorTemplates/Settings.cshtml
):
@model Setting
<div class="control-group">
<label class="control-label">@Html.LabelFor(x => x.Name)</label>
<div class="controls">
@Html.EditorFor(model => model.Value)
</div>
</div>
现在一切都按惯例进行。无需编写任何 foreach 循环。这@Html.EditorFor(x => x.Settings)
Index 视图中的调用分析视图模型的 Settings 属性,并检测到它是某个其他模型的集合(Setting
在这种情况下)。因此它将开始循环遍历这个集合并搜索相应的编辑器模板(~/Views/Settings/EditorTemplates/Setting.cshtml
) 将自动为该集合的每个元素呈现。因此,您甚至不需要在视图中编写任何循环。除了简化代码之外,现在Html.EditorFor(x => x.Value)
编辑器模板中将为输入字段生成正确的名称。