![](/img/trans.png)
[英]How do I make ASP.NET MVC 3 render objects with complex types in the auto-generated views?
[英]How to structure creation views for complicated objects in asp.net mvc?
我正在使用Visual Studio Community 2017学习asp.net mvc,作为一种教学项目,我正在制作一个Web应用程序,以跟踪锻炼结果。 我的模型由WorkOut对象组成,该对象具有一个Exercise列表(或更具体地讲是ICollection),并且每个Exercise都有一个ICollection。 这是我的模型课程的基础。
public class WorkOut
{
public int Id { get; set; }
public int Length { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<Exercise> ExerciseList { get; set; }
}
public class Exercise
{
public int Id { get; set; }
public string Name { get; set; }
public int WorkOutId { get; set; }
public virtual WorkOut WorkOut { get; set; }
public virtual ICollection<RepUnit> Sets { get; set; }
}
public class RepUnit
{
public int Id { get; set; }
public int Rep { get; set; }
public int? Weight { get; set; }
public int ExerciseId { get; set; }
public virtual Exercise Exercise { get; set; }
}
使用WorkOut作为模型自动生成视图会导致“创建”操作和仅生成Length和Date属性的视图。 通常,自动生成的视图和控制器仅添加非虚拟属性。 所以我想也许我必须做一个多步骤的创建过程。 创建锻炼,创建锻炼并向其添加代表,将该锻炼添加至锻炼中,停止或添加其他锻炼。 所以我想出ID让VS为我完成一些工作,然后为每个模型对象类型生成器(WorkOutsController,ExportsController,RepUnitsController)制作控制器和视图,然后我将修剪掉那些不需要的视图,甚至重构动作。实际使用到新的控制器中。 所以WorkOutsController我的POST动作是这个。
public ActionResult Create([Bind(Include = "Id,Length,Date")] WorkOut workOut)
{
if (ModelState.IsValid)
{
db.WorkOuts.Add(workOut);
db.SaveChanges();
return RedirectToAction("Create","Exercises",new { workoutId = workOut.Id });
}
return View(workOut);
}
因此,我将exerciseId携带到Exercise控制器中,但是我不确定该如何进行。 我想继续携带外表的id,然后在下一步中给练习命名,并显示刚刚添加的相关日期。 我唯一想做的就是像这样在ExerciseController的GET操作中实例化一个Exercise。
public ActionResult Create(int workoutID)
{
Exercise ex= new Exercise();
ex.WorkOutId=workoutID;
ex.WorkOut=db.WorkOuts(workoutID);
return View(ex);
}
这似乎很糟糕,在任何示例中我都没有看到这样的结果,但是它似乎可以工作。 相同的运动对象被带回我的POST创建动作中
public ActionResult Create([Bind(Include = "Id,Name,WorkOutId")] Exercise exercise)
{
if (ModelState.IsValid)
{
db.Exercises.Add(exercise);
db.SaveChanges();
return RedirectToAction("Create", "RepUnits", new { exerciseId = exercise.Id });
}
return View(exercise);
}
如您所见,它将调用RepUnits控制器和相关的Create动作。 在那里,我做一些非常相似的事情。 创建一个rep对象并将其传递给视图,从本质上讲,我添加了代表,直到完成为止。 最终,我将创建导航以返回以添加新练习或返回到索引视图。
综上所述,传递整个对象似乎很浪费,也许我的整个实现是错误的,所以我应该尝试以某种形式完成所有这一切。 到目前为止,谷歌搜索还没有找到我很多,因为我不确定要问什么问题,但是这篇文章在ASP.NET MVC应用程序中使用表单数据创建对象只是在类似的问题对话框中弹出,并且所讨论的应用程序是巧合的非常相似! 但是,当OP提及传递来回的ExerciseId时,该如何实现? 我以为可能要使用ViewBag,但如何获取视图来处理此ID? 我不得不尝试,例如
public ActionResult Create(int workoutId)
{
ViewBag.WoID = workoutId;
return View();
}
在ExercisesController中,然后在关联的Create视图中:
@Html.Hidden("WorkOutId", new { ViewBag.WoID })
但是后来在视图中,当我尝试引用锻炼日期时,它变成空白
<div class="form-group">
@Html.LabelFor(model => model.WorkOut.Date, "Work Out On:", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DisplayFor(model=>model.WorkOut.Date)
</div>
</div>
我是否应该在视图中执行以下操作:@ Model.WorkOutId = ViewBag.WoID;
哪个由于某种原因不起作用(编译器错误消息:CS1525:无效的表达式术语'='),但是那是沿着我如何传递这些ID的路线呢?
脚手架的视图是故意简化的。 处理相关项目需要多个注意事项,而Visual Studio不会为您解决这些问题。 但是,您可以并且非常鼓励根据您的特定需求更改支架视图。
例如,要在与锻炼相同的视图中创建锻炼,只需为“ Exercise
生成字段,其名称将允许Modelbinder绑定发布的数据。 对于集合属性,这意味着类似于CollectionProperty[N].Property
,其中N
是索引。
例如,您可以通过以下三个练习来初始化锻炼:
var model = new Workout
{
ExerciseList = new List<Exercise>
{
new Exercise(),
new Exercise(),
new Exercise()
}
};
然后,在您看来:
@for (var i = 0; i < Model.Exercises.Count(); i++)
{
@Html.EditorFor(m => m.ExerciseList[i].Name)
}
但是,这里需要注意一件事: ICollection
不可索引。 因此,要以这种方式执行此操作,您需要输入一个类型为List<Exercise>
的属性。 这是视图模型非常方便的地方。 但是,有一种解决方法:可以在Excercises
集合上使用EditorFor
。 例如,上面的代码将减少为:
@Html.EditorFor(m => m.ExerciseList)
EditorFor
是“模板化的帮助程序”,这意味着它使用模板来呈现传递给它的内容。 值得庆幸的是,它有一些默认值,因此,在某种程度上,您不必担心,但它会出现问题。 例如,在这里,剃刀将简单迭代中的项目ExerciseList
并呈现为模板Exercise
每一个。 由于Exercise
是自定义类型,并且没有默认模板,因此它将自检该类并为Exercise
上的每个公共属性呈现一个模板。 有时,这很好用。 例如,“ Name
将按原样使用文本框呈现。 但是,您还将获得Id
和WorkoutId
文本框,甚至不想在表单上看到它们。
您可以通过为Exercise
创建自己的编辑器模板,将视图添加到Views\\Shared\\EditorTemplates\\Exercise.cshtml
来解决此问题。 然后,该视图将具有一个Exercise
的模型,并且仅包含name属性的文本框。 然后,当您使用EditorFor
上ExerciseList
如上所述,它会呈现每个Exercise
利用了这一观点。
尽管如此,您可能已经意识到这仍然有些局限性:您必须通过一定数量的练习进行初始化,然后就可以了。 这就是JavaScript的用处。您无需遍历预定义的练习列表,而可以根据需要简单地动态添加新的练习字段块(或删除现有的块)。 但是,为此任务手动编写JavaScript会非常费力和繁琐。 在这一点上,最好使用诸如Knockout.js,Angular之类的东西。 这些库除其他外,为您提供双向数据绑定,因此您可以简单地为一个运动字段块的外观设置一个JavaScript模板,然后将其绑定到JavaScript对象的ExceriseList
成员(您的客户端-侧面的“视图模型”)。 然后,您可以简单地通过在此JS数组中添加或删除项目来重复这些字段。 显然,这还有很多,但这是基本框架。 您需要查阅所用库的各个文档,以确定确切地设置所有内容。
然后,您也可以冲洗并重复其他所有级别的关系。 换句话说,完全有可能一次发布包含多个练习的锻炼的整个对象图,每个练习具有多个rep单位,等等。所有这些都可以在一个视图中一次性完成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.