情境: 我有一个表,其中包含从ViewModel填充的行。每行都有一个复选框,允许用户检查一行或多行,然后从下拉菜单中的操作中进行选择,以对所选行的属性进行编辑。
到目前为止,一切正常,我可以让ViewModel正确传递,然后在POST Action方法中使用它及其所有属性。我可以根据用户选择的选项进行更改。
但是,由于下拉列表中的某些选项会产生相当大的且不可逆转的更改,因此我使用GET调用新视图并使用所选行填充新表,并要求用户确认他们想要做出改变。到目前为止,一切都还是不错的。新视图按预期填充,仅包含在上一个视图中选择的行。
问题: 在用户确认其意图后,使用POST调用Action方法。正确填充当前View的ViewModel正确地进入控制器。我得到ViewModel,但没有与填充View的属性相同的属性。
视图模型
public class ProjectIndexViewModel
{
public List<ProjectDetailsViewModel> Projects { get; set; }
public string FlagFormEditProjects { get; set; }
public string FlagFormNewProjectStatus { get; set; }
}
List<ProjectDetailsViewModel> Projects
用于填充表中的行,而Projects是控制器中POST Action方法中未正确绑定的内容。
选择复选框的初始视图。请注意选择其中一个下拉选项时调用的其中一个javascript函数的示例,即提交表单的内容。
@using (Html.BeginForm("EditProjectsTable", "Project", FormMethod.Get, new { name = "formEditProjects", id = "formEditProjects" }))
{
@Html.HiddenFor(item => item.FlagFormEditProjects)
@Html.HiddenFor(item => item.FlagFormNewProjectStatus)
....
<table>
<thead>
....
</thead>
<tbody>
@for (int i = 0; i < Model.Projects.Count; i++)
{
<tr>
<td>@Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td>
<td>@Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td>
.... // more display properties
<td>
@Html.CheckBoxFor(x => x.Projects[i].Selected, new { @class = "big-checkbox" })
@Html.HiddenFor(x => x.Projects[i].ProjectModelId)
</td>
</tr>
}
</tbody>
</table>
}
function submitFormRemoveProjects() {
$("#FlagFormEditProjects").attr({
"value": "RemoveProjects"
});
$('#formEditProjects').submit();
}
返回“确认”的操作方法查看(工作正常)
[HttpGet]
[Authorize(Roles = "Sys Admin, Account Admin, User")]
public async Task<ActionResult> EditProjectsTable([Bind(Include = "Projects,FlagFormEditProjects,FlagformNewProjectStatus")]ProjectIndexViewModel projectIndexViewModel)
{
// Repopulate the Projects collection of ProjectIndexViewModel to
// include only those that have been selected
return View(projectIndexViewModel);
}
从上面的Action方法返回的视图(正常工作)请注意,调用的Action方法是使用Html.BeginForm调用中的actionName变量动态设置的。
@using (Html.BeginForm(actionName, "Project", FormMethod.Post))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.FlagFormNewProjectStatus)
....
<table>
<thead>
....
</thead>
<tbody>
@for (int i = 0; i < Model.Projects.Count; i++)
{
<tr>
<td>@Html.HiddenFor(x => x.Projects[i].ProjectModelId)</td>
<td>@Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td>
<td>@Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td>
.... // more display properties
</tr>
}
</tbody>
</table>
<input type="submit" value="Delete Permanently" />
}
从此View 调用的一个Controller Action方法的示例,它与View中没有相同的Project。不知何故,它具有与最初选择的项目数相同的项目,但如果只选择了一个项目,则项目具有最低的模型ID。我不确定如何描述正在发生的事情。但总的来说,正确的ViewModel并没有进入下面显示的POST方法示例。
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Sys Admin, Account Admin")]
public async Task<ActionResult> DeleteConfirmedMultipleProjects([Bind(Include = "Projects")] ProjectIndexViewModel projectIndexViewModel)
{
if (ModelState.IsValid)
{
// Remove Projects from db and save changes
return RedirectToAction("../Project/Index");
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
请帮忙!
答案 0 :(得分:1)
问题是,当您从第一个视图提交到EditProjectsTable()
方法时,所有表单控件的值都会添加到ModelState
。
重新填充ProjectDetailsViewModel
的集合不会更新ModelState
,当您返回视图时,DisplayFor()
方法会显示正确的值,因为DisplayFor()
使用的值模型,不过你的
@Html.HiddenFor(x => x.Projects[i].ProjectModelId)
将使用ModelState
中的值,生成表单控件的所有HtmlHelper
方法也是如此(PasswordFor()
除外)。
解决此问题的一种方法是在ModelState.Clear()
方法中返回视图之前调用EditProjectsTable()
。 HiddenFor()
方法现在将使用模型的值,因为没有ModelState
值。
[HttpGet]
[Authorize(Roles = "Sys Admin, Account Admin, User")]
public async Task<ActionResult> EditProjectsTable(ProjectIndexViewModel projectIndexViewModel)
{
// Repopulate the Projects collection of ProjectIndexViewModel to
// include only those that have been selected
ModelState.Clear(); // add this
return View(projectIndexViewModel);
}
有关这是默认行为的原因的解释,请参阅this answer的第二部分。
附注:您使用的是视图模型,因此您的方法中不包含[Bind]
属性。
答案 1 :(得分:0)
我认为你的问题来自这部分:
@Html.CheckBoxFor(x => x.Projects[i].Selected, new { @class = "big-checkbox" })
@Html.HiddenFor(x => x.Projects[i].ProjectModelId)
之前我遇到过这个错误,我做的是向ProjectDetailsViewModel
添加一个布尔属性,如IsSelected
。
那么你应该:
@Html.CheckBoxFor(x => x.Projects[i].IsSelected, new { @class = "big-checkbox" })
然后在方法上你应该添加:
foreach (var project in ProjectIndexViewModel.Projects )
{
if (project.IsSelected==true)
"put your logic here"
}