Post 上的 MVC 集合缺少项目
MVC Collection Missing items on Post
我最近 运行 了解了 MVC4 的一些行为,希望能对此有所了解。我的实际项目中的行为使用了更复杂的对象,但我能够用简单的对象复制它,这就是我在这个问题中使用的对象。我的情况如下:
- 我有一个使用字符串集合作为模型的视图。
- 我正在调用 "EditorFor" 以显示集合中的每个字符串。
- 在控制器中,我用 7 个字符串项填充模型并将其传递给视图。
- 视图呈现所有 7 个项目。
- 我正在使用 jquery 从 DOM 中删除集合中的第二个项目。
- 我点击提交到post回来。
- 我检查了我的 posted 返回模型的内容,它只包含列表中的第一项。我希望列表中有 6 个项目(我从 DOM 中删除的项目是 7 分钟)。
所描述情况的代码如下:
查看
@model ICollection<string>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
@Html.EditorFor(x => x);
<input id="btnSubmit" type="submit" value="Submit" />
}
控制器
[HttpGet]
public ActionResult Index()
{
List<string> vm = new List<string>
{
"one",
"two",
"threE",
"four",
"five",
"six",
"seven"
};
return View(vm);
}
[HttpPost]
public ActionResult Index(List<string> vm)
{
return View(vm);
}
感谢所有对此提供帮助的人。期待增加我对 MVC 的理解!
这是一个索引问题。
@model ICollection<string>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
@for(int index = 0; index < Mode.Count; i++) {
@Html.EditorFor(x => x[index])
}
<input id="btnSubmit" type="submit" value="Submit" />
}
如果计划删除项目,棘手的部分将是管理剩余的索引。
这是列表绑定问题。
这是关于 mvc 中的列表绑定的精彩文章:Model Binding To A List
使用 Masound post 编辑的答案中的信息,我能够创建解决此问题的方法。事实证明,问题是我使用的是顺序集合,其中每个索引都依赖于存在的前一个索引。因此,当我从集合中删除一个元素时,我将 link 丢失给了继续它的其他项目。
解决方案是使用非顺序集合,其中索引作为隐藏字段存储在 DOM 中。使用此方法,MVC 知道每个索引所在的位置,并可以在 post 上用所有元素重新创建集合。
解决方案说明如下:
- 我有一个使用测试对象集合作为其模型的视图。
- 我有一个助手 class 可以输出集合中对象的索引。
- 我正在调用 "EditorFor" 以显示每个测试对象及其在集合中的索引。
- 在控制器中,我用 7 个字符串项填充模型并将其传递给视图。
- 视图呈现所有 7 个项目。
- 我正在使用 jquery 从 DOM 中删除集合中的第二个项目。
- 我点击提交到post回来。
- 我检查了我的 posted 返回模型的内容,它只包含 6 个项目,这正是我所期望的。
- 我正在重新定向到 Get 方法以刷新模型状态的绑定。我注意到索引会绑定到集合中的错误项目的行为。因此,如果您打算重新加载相同的视图,那么我建议实施此解决方案以避免意外行为。
本方案代码如下:
Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace WebApplication2.Models
{
public class Test
{
[Required(ErrorMessage="Required")]
public string str {get; set;}
}
}
Test.cshtml
@model WebApplication2.Models.Test
@using WebApplication2.Helpers
@{
ViewBag.Title = "Test";
}
<div id="@Model.str">
@Html.TextBoxFor(x => x.str)
@Html.ValidationMessageFor(x => x.str)
@Html.HiddenIndexerInputForModel()
</div>
Index.cshtml
@model ICollection<WebApplication2.Models.Test>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
@Html.EditorFor(x => x)
<input id="btnSubmit" type="submit" value="Submit" />
}
控制器
[HttpGet]
public ActionResult Index()
{
List<Test> vm = (List<Test>)Session["Test"];
if (vm == default(List<Test>))
{
vm = new List<Test>
{
new Test { str="one"},
new Test { str="two"},
new Test { str="three"},
new Test { str="four"},
new Test { str="five"},
new Test { str="six"},
new Test { str="seven"}
};
Session.Add("Test", vm);
}
return View(vm);
}
[HttpPost]
public ActionResult Index(List<Test> vm)
{
Session.Add("Test", vm);
return RedirectToAction("Index");
}
HtmlHelper.cs - Helper 取自此处:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
public static class ListModelBindingExtensions
{
static Regex _stripIndexerRegex = new Regex(@"\[(?<index>\d+)\]", RegexOptions.Compiled);
public static string GetIndexerFieldName(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
fieldName = _stripIndexerRegex.Replace(fieldName, string.Empty);
if (fieldName.StartsWith("."))
{
fieldName = fieldName.Substring(1);
}
return fieldName;
}
public static int GetIndex(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
var match = _stripIndexerRegex.Match(fieldName);
if (match.Success)
{
return int.Parse(match.Groups["index"].Value);
}
return 0;
}
public static MvcHtmlString HiddenIndexerInputForModel<TModel>(this HtmlHelper<TModel> html)
{
string name = html.ViewData.TemplateInfo.GetIndexerFieldName();
object value = html.ViewData.TemplateInfo.GetIndex();
string markup = String.Format(@"<input type=""hidden"" name=""{0}"" value=""{1}"" />", name, value);
return MvcHtmlString.Create(markup);
}
}
感谢为这个解决方案的到来提供帮助的所有人!
可能与 Strange behaviour in ASP.NET MVC: removing item from a list in a nested structure always removes the last item
重复
在回传中从列表中删除项目时,我不需要验证,所以我使用
ModelState.Clear();
在返回视图的往返过程中的任何地方,这解决了我在这种情况下的问题。
我最近 运行 了解了 MVC4 的一些行为,希望能对此有所了解。我的实际项目中的行为使用了更复杂的对象,但我能够用简单的对象复制它,这就是我在这个问题中使用的对象。我的情况如下:
- 我有一个使用字符串集合作为模型的视图。
- 我正在调用 "EditorFor" 以显示集合中的每个字符串。
- 在控制器中,我用 7 个字符串项填充模型并将其传递给视图。
- 视图呈现所有 7 个项目。
- 我正在使用 jquery 从 DOM 中删除集合中的第二个项目。
- 我点击提交到post回来。
- 我检查了我的 posted 返回模型的内容,它只包含列表中的第一项。我希望列表中有 6 个项目(我从 DOM 中删除的项目是 7 分钟)。
所描述情况的代码如下:
查看
@model ICollection<string>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
@Html.EditorFor(x => x);
<input id="btnSubmit" type="submit" value="Submit" />
}
控制器
[HttpGet]
public ActionResult Index()
{
List<string> vm = new List<string>
{
"one",
"two",
"threE",
"four",
"five",
"six",
"seven"
};
return View(vm);
}
[HttpPost]
public ActionResult Index(List<string> vm)
{
return View(vm);
}
感谢所有对此提供帮助的人。期待增加我对 MVC 的理解!
这是一个索引问题。
@model ICollection<string>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
@for(int index = 0; index < Mode.Count; i++) {
@Html.EditorFor(x => x[index])
}
<input id="btnSubmit" type="submit" value="Submit" />
}
如果计划删除项目,棘手的部分将是管理剩余的索引。
这是列表绑定问题。
这是关于 mvc 中的列表绑定的精彩文章:Model Binding To A List
使用 Masound post 编辑的答案中的信息,我能够创建解决此问题的方法。事实证明,问题是我使用的是顺序集合,其中每个索引都依赖于存在的前一个索引。因此,当我从集合中删除一个元素时,我将 link 丢失给了继续它的其他项目。
解决方案是使用非顺序集合,其中索引作为隐藏字段存储在 DOM 中。使用此方法,MVC 知道每个索引所在的位置,并可以在 post 上用所有元素重新创建集合。
解决方案说明如下:
- 我有一个使用测试对象集合作为其模型的视图。
- 我有一个助手 class 可以输出集合中对象的索引。
- 我正在调用 "EditorFor" 以显示每个测试对象及其在集合中的索引。
- 在控制器中,我用 7 个字符串项填充模型并将其传递给视图。
- 视图呈现所有 7 个项目。
- 我正在使用 jquery 从 DOM 中删除集合中的第二个项目。
- 我点击提交到post回来。
- 我检查了我的 posted 返回模型的内容,它只包含 6 个项目,这正是我所期望的。
- 我正在重新定向到 Get 方法以刷新模型状态的绑定。我注意到索引会绑定到集合中的错误项目的行为。因此,如果您打算重新加载相同的视图,那么我建议实施此解决方案以避免意外行为。
本方案代码如下:
Test.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace WebApplication2.Models
{
public class Test
{
[Required(ErrorMessage="Required")]
public string str {get; set;}
}
}
Test.cshtml
@model WebApplication2.Models.Test
@using WebApplication2.Helpers
@{
ViewBag.Title = "Test";
}
<div id="@Model.str">
@Html.TextBoxFor(x => x.str)
@Html.ValidationMessageFor(x => x.str)
@Html.HiddenIndexerInputForModel()
</div>
Index.cshtml
@model ICollection<WebApplication2.Models.Test>
@{
ViewBag.Title = "Home Page";
}
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
@Html.EditorFor(x => x)
<input id="btnSubmit" type="submit" value="Submit" />
}
控制器
[HttpGet]
public ActionResult Index()
{
List<Test> vm = (List<Test>)Session["Test"];
if (vm == default(List<Test>))
{
vm = new List<Test>
{
new Test { str="one"},
new Test { str="two"},
new Test { str="three"},
new Test { str="four"},
new Test { str="five"},
new Test { str="six"},
new Test { str="seven"}
};
Session.Add("Test", vm);
}
return View(vm);
}
[HttpPost]
public ActionResult Index(List<Test> vm)
{
Session.Add("Test", vm);
return RedirectToAction("Index");
}
HtmlHelper.cs - Helper 取自此处:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
public static class ListModelBindingExtensions
{
static Regex _stripIndexerRegex = new Regex(@"\[(?<index>\d+)\]", RegexOptions.Compiled);
public static string GetIndexerFieldName(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
fieldName = _stripIndexerRegex.Replace(fieldName, string.Empty);
if (fieldName.StartsWith("."))
{
fieldName = fieldName.Substring(1);
}
return fieldName;
}
public static int GetIndex(this TemplateInfo templateInfo)
{
string fieldName = templateInfo.GetFullHtmlFieldName("Index");
var match = _stripIndexerRegex.Match(fieldName);
if (match.Success)
{
return int.Parse(match.Groups["index"].Value);
}
return 0;
}
public static MvcHtmlString HiddenIndexerInputForModel<TModel>(this HtmlHelper<TModel> html)
{
string name = html.ViewData.TemplateInfo.GetIndexerFieldName();
object value = html.ViewData.TemplateInfo.GetIndex();
string markup = String.Format(@"<input type=""hidden"" name=""{0}"" value=""{1}"" />", name, value);
return MvcHtmlString.Create(markup);
}
}
感谢为这个解决方案的到来提供帮助的所有人!
可能与 Strange behaviour in ASP.NET MVC: removing item from a list in a nested structure always removes the last item
重复在回传中从列表中删除项目时,我不需要验证,所以我使用
ModelState.Clear();
在返回视图的往返过程中的任何地方,这解决了我在这种情况下的问题。