简单的问题,但我无法弄清楚缺少什么。我有一个简单的ViewModel(它会变大):
public class TigerTrackingViewModel
{
public TigerTrackingViewModel()
{
this.TigerTrail = new TigerTrail();
}
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public TigerTrail TigerTrail { get; set; }
}
TigerTrail是一个嵌套对象。以下是所有属性和子属性:
public class TigerTrail
{
public TigerTrail()
{
DoneDate = new DateTime(1950, 01, 01);
TigerTrailRequiredBadges = new Collection<TigerTrailRequiredBadge>();
TigerTrailElectivedBadges = new Collection<TigerTrailElectiveBadge>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TigerTrailRequiredBadge> TigerTrailRequiredBadges { get; set; }
public virtual ICollection<TigerTrailElectiveBadge> TigerTrailElectivedBadges { get; set; }
//public virtual ICollection<Youth> Youth { get; set; }
public bool? Done { get; set; }
public DateTime? DoneDate { get; set; }
}
所以它有TigerTrailRequiredBadges:
public class TigerTrailRequiredBadge
{
public TigerTrailRequiredBadge()
{
DoneDate = new DateTime(1950, 01, 01);
TigerTrailRequiredBadgeSubRequirements = new Collection<TigerTrailRequiredBadgeSubRequirement>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<TigerTrailRequiredBadgeSubRequirement> TigerTrailRequiredBadgeSubRequirements { get; set; }
public bool Done { get; set; }
public DateTime DoneDate { get; set; }
}
并且有TigerTrailRequiredBadgeSubRequirement(s):
public class TigerTrailRequiredBadgeSubRequirement
{
public TigerTrailRequiredBadgeSubRequirement()
{
DoneDate = new DateTime(1950, 01, 01);
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string ShortCode { get; set; }
public string Type { get; set; } //Family, Den, Go See It
public string Description { get; set; }
public bool Done { get; set; }
public DateTime DoneDate { get; set; }
}
回到TigerTrail.cs课程中,还有Elective Badge课程:
public class TigerTrailElectiveBadge
{
public TigerTrailElectiveBadge()
{
DoneDate = new DateTime(1950, 01, 01);
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int Number { get; set; }
public string Name { get; set; }
public string Requirement { get; set; }
public bool Done { get; set; }
public DateTime DoneDate { get; set; }
}
因此,我的ViewModel将提供所有可用的属性。不幸的是,大部分都需要。它很大而且很难看,但我必须让它发挥作用。
在Controller GET方法中:
public ActionResult TigerTrail()
{
var vm = new List<TigerTrackingViewModel>();
var pack = Ctx.CubPacks.FirstOrDefault(x => x.Id == PackId);
var permTrail = Ctx.TigerTrails.FirstOrDefault(x => x.Name.Contains("PERM"));
foreach (var youth in pack.Youths)
{
//if anyone does not have this trail set up, make a new one.
if (youth.TigerTrail == null)
{
youth.TigerTrail = new TigerTrail();
if (youth.TigerTrail.TigerTrailElectivedBadges == null)
{
youth.TigerTrail.TigerTrailElectivedBadges = new Collection<TigerTrailElectiveBadge>();
}
if (youth.TigerTrail.TigerTrailRequiredBadges == null)
{
youth.TigerTrail.TigerTrailRequiredBadges = new Collection<TigerTrailRequiredBadge>();
}
youth.TigerTrail = permTrail;
}
youth.TigerTrail.Name = youth.FirstName + " " + youth.LastName + " Tiger Trail";
vm.Add(new TigerTrackingViewModel
{
FirstName = youth.FirstName,
LastName = youth.LastName,
YouthGuid = youth.YouthGuid,
TigerTrail = youth.TigerTrail
});
}
return View(vm);
}
post方法中的:
[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
return View();
}
每次回发都会返回null。以下是观点:
@model List<eTrail.Cubs.ViewModels.TigerTrackingViewModel>
@{
ViewBag.Title = "Award Tracking";
Layout = "~/Areas/App/Views/Shared/_BackendDashboard.cshtml";
}
<h1>Award Tracking</h1>
<hr />
<div class="row">
<div class="span12">
@using (Html.BeginForm("TigerTrail", "Awards", FormMethod.Post, new { @class = "form-horizontal" }))
{
for (int i = 0; i < Model.Count; i++)
{
@Html.HiddenFor(x => Model[i].TigerTrail)
foreach (var item in Model[i].TigerTrail.TigerTrailElectivedBadges)
{
@Html.DisplayFor(x => item.Done)
@Html.DisplayFor(x => item.DoneDate)
@Html.DisplayFor(x => item.Id)
@Html.DisplayFor(x => item.Name)
@Html.DisplayFor(x => item.Number)
@Html.DisplayFor(x => item.Requirement)
}
}
<input type="submit" value="submit" />
}
</div>
</div>
我添加了所有HiddenFor字段,因为有人建议他们都需要在那里才能发回。仍然没有运气。如果我在页面上查看源代码,则id / name将如下所示:
<li>
<input id="elec_Done" name="elec.Done" type="checkbox" value="true"><input name="elec.Done" type="hidden" value="false">
<b>Pet Care</b>
Visit a veterinarian or animal groomer
<input id="elec_Number" name="elec.Number" type="hidden" value="43">
<input id="elec_DoneDate" name="elec.DoneDate" type="hidden" value="1/1/1950 12:00:00 AM">
</li>
翻译中有什么损失?如何将List返回控制器?
修改
基于两个答案,因为我需要澄清这个问题:在httppost方法中,我应该接收的列表是空的。
[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths) //this is what is null on postback.
{
. . . Do work with youths . .
return RedirectToAction(...);
}
答案 0 :(得分:7)
我已根据您的代码成功运行了这项工作。您的HttpGet方法视图的修复形式为:
@using (Html.BeginForm("TigerTrail", "Awards", FormMethod.Post, new { @class = "form-horizontal" }))
{
for (int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(x => x[i].FirstName)
for (int j = 0; j < Model[i].TigerTrail.TigerTrailElectivedBadges.Count; ++j)
{
@Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Name)
}
}
<input type="submit" value="submit" />
}
然后你可以取出你添加的很多集合初始化。 (顺便说一下,那里有一个错误 - 行youth.TigerTrail = permTrail;
完全替换了前面空的但初始化的对象。)
简短说明
诀窍是使用EditorFor
或HiddenFor
将所有字段包含在内,因为所有其他字段都为null / blank / default / empty。这些天不需要为MVC模型绑定初始化集合,它们将自动创建如果其中包含某些内容(即使用EditorFor
或HiddenFor
)。 DisplayFor
不会导致值往返;如果没有对象的值进行旅行,则该对象不会进入该集合;如果一个集合中没有任何项目进行往返,除非你强制收集空集,否则该集合不会在回发中出现。
语法也很重要:所有索引(foo[i].bar[j].baz
)都必须在里面你提供的lambda(,即在=>
的右侧在结束括号之前)。如:
@Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Name)
// ^^^ ^^^
要实现这一点,您必须从ICollection<T>
和Collection<T>
更改为IList<T>
和List<T>
,以便您可以为徽章编制索引。< / p>
更长的解释
正如其他评论者提到的那样,除非你编写自己的模型绑定器,否则从HttpGet方法,通过其视图形式到HttpPost方法的往返数据的唯一数据是:
<input ...>
标记是可见还是隐藏),和 DefaultModelBinder
可理解,这意味着input
的{{1}}属性必须采用特定格式。这意味着您必须坚持使用name
可以使用的类型,并注意DefaultModelBinder
,EditorFor
等,尤其是对于集合的集合。
因此,如果您希望任何TigerTrailViewModel的TigerTrail成员完成此任务,则需要为每个成员指定HiddenFor
或EditorFor
。您不需要对象本身的隐藏字段(例如HiddenFor
。您确实需要在其他方面要查看其成员的字段。如果您想要TigerTrails和选择性徽章列表跨越,然后对于您想要查看的徽章的每个字段,您必须使用上述语法确保这些字段为@Html.HiddenFor(x => x.TigerTrail)
或EditorFor
。
作为脚注,如果类型匹配,HiddenFor
或任何其他xxxxFor方法也可以正常工作。
为什么你必须将所有内容都放在lambda中(TextBoxFor
的右边)?这归结为MVC使用表达式树,除非你对此感兴趣,否则可能不值得担心,但是:你在调用中提供的lambda =>
或类似的变为表达式树而不是委托,MVC在运行时解析这个编译器生成的数据结构,以计算如何为它创建的输入写出EditorFor
属性,它可以在以后回读的方式。
如果您使用name
,请执行以下操作:
EditorFor
然后MVC将创建输入la:
@Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Done)
该名称语法是MVC&#39; <input class="check-box" name="[6].TigerTrail.TigerTrailElectivedBadges[28].Done" type="checkbox" value="true" />
所需的语法。
顺便提一下,如果你真的想要,你可以用这些名称格式手动生成输入标签,而不是使用DefaultModelBinder
等。这对于用户将项目添加到集合的应用程序非常有用,如果您有JavaScript,则可以动态添加相关的表单字段。如果名称格式正确,MVC将在回发时找到它们。
建议(FWIW,可能不多)
MVC不像Web窗体,因此没有视图状态会自动保存页面上的每个项目以供日后使用。这是一件好事,因为它可以加速您的应用程序,从而加快加载速度并减少不必要的互联网流量。一般来说,良好的做法是仅包含用户可能更改的数据,或者只需了解用户提供的其他值(例如,唯一的记录ID,这将有助于您在用户正在处理的数据库。)
如果您想在回发时显示或使用其他数据,通常最好再次查找(基于用户提供的ID等或隐藏字段中包含的ID等)在不经意间往返发送它。
因此,如果你的嵌套馆藏不能在回程中重新开始,那么除非用户能够编辑这些数据,否则它并不重要;因为如果你真的需要它,你可以再次将它从数据库中拉出来。
对于某些应用程序(不是最常见的MVC应用程序),您可能想要传输对象的整个状态(例如,如果您无法更改多个应用程序)多用户可能正在编辑的数据库表的用户不友好模式,但是您希望防止它们覆盖彼此,通过保存所有值,检查差异并拒绝编辑(如果它们发生冲突)来工作。在这种情况下,您必须传输往返的所有数据,并且您需要使用EditorFor
和正确的语法或您自己设计的其他机制来执行此操作。
答案 1 :(得分:0)
看起来您需要添加表单字段以绑定到Pack
属性。否则将没有任何内容回发给控制器。 http是无状态的,因此原始模型对象不会被保留,因此必须从表单字段
答案 2 :(得分:0)
您需要的答案很简单,您不需要使用隐藏字段或其他东西。
public ActionResult TigerTrail()
{
var vm = new List<TigerTrackingViewModel>();
//other codes
return View(vm) //Here is the important for you. Returning something inside View()is to show the Model and data.
}
使用HttpPost的其他视图在View();
中没有返回任何内容[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
return View(); //Here is where you did not give any data/model to the View. So, if you don't return something inside View(), you can not present any data or can not use model in the View.
}
您所要做的就是:
[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
return View(youths); //It will work.
}
如果您还有其他需要,请告诉我。
答案 3 :(得分:0)
[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
return View();
}
我假设您的列表不为空,因此您只是忘记将模型传递给视图。像这样:
return View(youths);
如果您需要在隐藏字段中编写许多内容,请考虑每次从数据库获取模型并仅使用隐藏字段ID。
答案 4 :(得分:0)
你可以改变一些事情:
1)在ViewModel中,最好在构造函数中初始化复杂属性。收藏品特别适用。不知何故,模型绑定器似乎无法做到这一点。这以Null属性结束。 我认为这不是你的情况,因为你的整个VM列表都是空的,但是一旦你解决了问题,就会出现这个问题。
public class TigerTrackingViewModel
{
public TigerTrackingViewModel(){
this.TigerTrail = new TigerTrail(); //Add this
}
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public TigerTrail TigerTrail { get; set; }
}
应该对所有复杂的属性进行此操作。
2)显示项目集合时,需要使用索引进行渲染,为此,请将foreach更改为正常情况:
@for (int i=0; i < Model.Count)
{
@Html.HiddenFor(x => Model[i].TigerTrail)
...
...
}
在您的情况下,给定要显示的大量数据,您应该删除所有视图代码以处理内部视图模型...并尝试获取已发布的VM列表。完成后,开始添加其他属性。
请记住:仅发布输入中的信息,因此...如果您需要发布的内容而不是,请检查您是否至少将其隐藏起来。要检查的另一件事是,您的索引([i]
)是否正确。
在这里,您可以看到一篇关于绑定到列表的优秀文章: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx(它有点陈旧,但基础知识仍然适用)