方案
我有一个父/子模型(确切地说是一个小问卷表格和一个或多个联系人)。由于历史原因,所有这些都是在同一个表单上完成的,因此用户可以拥有父表单和一个孩子的表单,他们会点击一个按钮来添加更多的孩子。孩子有一些标准的领域和父母一样,没什么特别的。主要要求是数据必须不会触及数据库,直到所有数据都有效并进行设置,而我必须返回服务器才能添加删除子项。
实施
在ASP.NET MVC中使用 MVC 2和VS 2010 非常快速。我有两个模型,一个用于父母,一个用于孩子,只有一个控制器。控制器有一个Create
方法get
,并获得一个默认视图,其中包含一个包含一个孩子的全新父级。我使用编辑器模板作为可以很好地运行的子模型。
我有一个HTML表单,其中包含“save”和“add child”,每个表单都有“删除”按钮。由于这不能存储在数据库中,我将临时模型存储在表单本身中,它在浏览器和服务器之间来回传递。 Perfromance在这里不是一个问题,但是开发成本因为这些形式中有很多 - 所以请不要因为建议采用其他方法而分散注意力,尽管我还是赞赏评论。
为了找出要删除的子项,我创建了临时GUID ID并将它们与子项关联起来。这将转到HTML输入的删除按钮值(当你有多个动作和相同的表格时通常的技巧)。
我禁用了缓存。
问题
请查看下面的代码段。我已经调试了代码,我看到总是正确的GUID被传递,正确的项目从控制器的列表中删除,并更正在模板中呈现的项目。但总是最后一个删除!我经常点击第一次删除,可以看到最后一次删除。我继续,第一项是最后一项被删除。
控制器
public ActionResult Create()
{
EntryForm1 entryForm1 = new EntryForm1();
entryForm1.Children.Add(new Child("FILL ME", "FILL ME"){ TempId = Guid.NewGuid()});
return View("EntryForm1View", entryForm1);
}
[HttpPost]
public ActionResult Create(EntryForm1 form1, FormCollection collection, string add)
{
if (add == "add")
form1.Children.Add(new Child("FILL ME", "FILL ME") {TempId = Guid.NewGuid()});
var deletes = collection.AllKeys.Where(s => s.StartsWith("delete_"));
collection.Clear();
if (deletes.Count() > 0)
{
string delete = deletes.FirstOrDefault();
delete = delete.Replace("delete_", "");
Guid g = Guid.Parse(delete);
var Children = form1.Children.Where(x => x.TempId == g).ToArray();
foreach (Child child in Children)
{
form1.Children.Remove(child);
}
// HERE CORRECT ITEM IS DELETED, BELIEVE ME!!
}
if (ModelState.IsValid)
{
return Redirect("/");
}
return View("EntryForm1View", form1);
}
查看摘要
<% for (int i = 0; i < Model.Children.Count;i++ )
{%>
<h4> <%: Html.EditorFor(m=>m.Children[i])%></h4>
<%
}%>
<p>
<input type="submit" value="Create" name="add" />
<input type="submit" value="add" name="add" />
</p>
子编辑器模板代码
<%: Html.HiddenFor(x=>x.TempId) %>
</span>
<input type="submit" name='delete_<%: Html.DisplayTextFor(m => m.TempId) %>' value="Delete" />
非常感谢您的时间和关注
更新
我被要求进行模型课程,我正在分享它们。
Entryform1
是父级,Somesing
是子级。
public Somesing()
{
}
public Somesing(string o, string a) : this()
{
OneSing = o;
AnozerSing = a;
}
[StringLength(2)]
public string OneSing { get; set; }
[StringLength(2)]
public string AnozerSing { get; set; }
public Guid TempId { get; set; }
}
public class EntryForm1
{
public EntryForm1()
{
Sings = new List<Somesing>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public List<Somesing> Sings { get; set; }
}
答案 0 :(得分:7)
我认为问题在于ModelState。当视图被渲染时,我认为是问题所在,在POST
之后,最后一个值不显示,即从视图中删除。
问题是Model.Children.Count
将返回要显示的正确数量的元素。
让我们打破这个......
所以如果你最初有5然后根据Guid删除了第一个在索引0的那个,你现在有4个项目,其中索引为1到4。
但是,在帖子之后渲染视图时,HtmlHelpers不会查看已发布模型中的值,而是查看ModelState中包含的值。因此,在ModelState中,索引为0的项仍然存在,并且由于循环现在循环为4,因此不会显示最后一个元素。
解决方案,使用ModelState.Clear()
答案 1 :(得分:2)
ModelState.Clear() 将解决此问题。
ModelState.Clear()用于清除错误,但它也用于强制MVC引擎重建要传递给View的模型。
答案 2 :(得分:1)
好的,正如艾哈迈德指出的那样,ModelState
是问题的关键。它包含这样的集合:
现在,如果我从列表中删除项目0,现在项目将在列表中向上移动,并且ModelState中的数据将与模型不同步。我原本以为ASP.NET MVC足够聪明,能够找到并重新排序,但这要求太多了。
我实际上实现了PRG(post-redirect-get)并且通过将模型保持在会话中,我能够显示正确的信息但是,这将删除集合中的所有验证以及模型本身是否有效,它会愉快地保存并重定向回到主页“/”。显然这是不可接受的。
所以一个解决方案是删除ModelState
中的所有项目,然后为模型本身添加一个新条目(密钥为EmptyString
)。如果您使用错误“项目已删除”填充它,这实际上可以正常工作,因为它将显示在验证摘要中。
另一个解决方案是手动更改模型状态中的项目,并根据新索引重新排列它们。这不容易,但可能。