繁体   English   中英

如何在ASP.NET MVC中构造复杂对象的创建视图?

[英]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将按原样使用文本框呈现。 但是,您还将获得IdWorkoutId文本框,甚至不想在表单上看到它们。

您可以通过为Exercise创建自己的编辑器模板,将视图添加到Views\\Shared\\EditorTemplates\\Exercise.cshtml来解决此问题。 然后,该视图将具有一个Exercise的模型,并且仅包含name属性的文本框。 然后,当您使用EditorForExerciseList如上所述,它会呈现每个Exercise利用了这一观点。

尽管如此,您可能已经意识到这仍然有些局限性:您必须通过一定数量的练习进行初始化,然后就可以了。 这就是JavaScript的用处。您无需遍历预定义的练习列表,而可以根据需要简单地动态添加新的练习字段块(或删除现有的块)。 但是,为此任务手动编写JavaScript会非常费力和繁琐。 在这一点上,最好使用诸如Knockout.js,Angular之类的东西。 这些库除其他外,为您提供双向数据绑定,因此您可以简单地为一个运动字段块的外观设置一个JavaScript模板,然后将其绑定到JavaScript对象的ExceriseList成员(您的客户端-侧面的“视图模型”)。 然后,您可以简单地通过在此JS数组中添加或删除项目来重复这些字段。 显然,这还有很多,但这是基本框架。 您需要查阅所用库的各个文档,以确定确切地设置所有内容。

然后,您也可以冲洗并重复其他所有级别的关系。 换句话说,完全有可能一次发布包含多个练习的锻炼的整个对象图,每个练习具有多个rep单位,等等。所有这些都可以在一个视图中一次性完成。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM