在Mvc4和Knockout中嵌套多个Views \ ViewModels

时间:2013-04-01 17:53:30

标签: design-patterns breeze single-page-application durandal hottowel

我有一个Mvc4 SPA项目,我正在努力,我需要能够将多个视图嵌套在彼此之间。视图和视图模型使用Durandal链接。

在Mvc3中,我之前通过在视图中的局部视图中使用局部视图,并将参数传递给部分来完成此操作。这样做我能够在一对多关系中有多个部分,而这些关系又有多个部分显示一对多关系,所有关系都链接回父视图。

先前在Mvc3 =

中使用的示例
Public class ParentsController
{
public ActionResult Parent(int id)
{
    Parent parent = Db.Parents.Find(id);
    ViewBag.ParentId = parent.Id;
    return View(parent)
}

public PartialViewResult Child(int id)
{
    Child childs = Db.Childs.Where(w => w.ParentId = id);
    return PartialView("ChildPartial", childs.ToList());
}

public PartialViewResult GrandChild(int id)
{
    GrandChild grandChilds = Db.GrandChilds.Where(w => w.ChildId = id);
    return PartialView("GrandChildPartial", grandChilds.ToList());
}
}

并查看类似

的内容
Parent.cshtml
@{
    ViewBag.Title = "Parent";
}
@Html.DisplayFor(Model.ParentName)
@{Html.RenderAction("ChildPartial", new { id = ViewBag.ParentId});}


ChildPartial.cshtml
@{
    ViewBag.Title = "Children";
}
{ foreach (var child in childs)
{
@{Html.DisplayFor(child.ChildsName)}
@{Html.RenderAction("GrandChildPartial", new { id = ViewBag.ChildId});}
}}


GrandChildPartial.cshtml
@{
    ViewBag.Title = "Grand Children";
}
{ foreach (var grandChild in GrandChilds)
{
@{Html.DisplayFor(grandChild.GrandChildsName)}
}}

同样,以上是我过去使用的模式的一个简短示例,不需要帮助。

我可以使用Breeze加载子项并从父视图模型中相对容易地显示子项,但是当我进入大孩子时,感觉就像我进入'意大利面条'代码并从一个视图模型加载太多。我认为正确的做法是为父母,孩子和孙子创建一个视图模型,将它们分开并使它们可以重复使用。

我的问题是,父视图模型是否应该加载孩子的视图模型,然后让孩子的视图模型加载大孩子的视图模型,或者是否应该有一个视图从一个单独的父级到子级到孙级组成和级联?即,一个视图应该单独加载3个视图模型,还是应该有一个专用的父视图模型,它调用所有三个视图模型并加载它们或多或少相互独立?我正在寻找最佳实践,因为SPA的想法对我来说相对较新。

2 个答案:

答案 0 :(得分:3)

现在宣布一个最佳实践还为时尚早......特别是当您的应用程序的实际工作流程和性能特征未知时(此时可能不可知)。

通常,我们认为每个VM加载所需内容而不是依靠主VM来提供下级VM会更好。这里的本能是你将它分解成单独的VM,这样他们就可以执行自己的职责......所以让他们去做。主服务器成为其子VM的协调者,并尽可能地远离其实现细节。

我们打算在下个月左右发布一个名为“ TempHire ”的新样本,根据“Hot Towel(ette)”,可能会提供一些指导。时机可能不适合你。但你可以找到code on GitHub),我将总结(并过度简化)与你的问题相关的样本特征:

  1. 主虚拟机与根实体关联(在您的情况下,是父节点)。

  2. 主VM接收专用于该根实体工作流的“datacontext”(工作单元[UoW]),并用根实体的ID标记。

  3. “工作单位管理器”按根实体ID创建和跟踪UoW。如果您要求父母1的UoW,它将为您提供跟踪的那个或为您创建一个新的。

  4. 这使得子VM可以轻松地与主服务器共享数据上下文,并以相当分离的方式彼此共享。主服务器不必通过一系列嵌套的VM传递UoW。相反,每个VM都注入“工作单元管理器”,并可以通过父ID向“工作单元管理器”询问UoW;它会得到“其他人都在使用”的UoW。

  5. 现在,任何虚拟机都可以通过向UoW询问该数据来加载所需的任何数据(或从UoW缓存中的数据中获益)。 VM不必“思考”它是第一个还是最后一个请求数据,数据是否在缓存中,是否在开始时或根据需要检索数据。 UoW可以封装这些细节,为虚拟机提供简单的“查询”方法。

  6. 当然,UoW应该提供适当的“刷新”方法来应对陈旧性......对每种应用中的每种类型都有不同的定义。

  7. 我们倾向于为每个任务“沙盒化”UoW,这些任务通常围绕根实体。因此,Parent-1的主VM具有其自己的UoW和其自己的EntityManager,而Parent-2的主VM具有其自己的具有其自己的EntityManager的UoW。通过这种方式,用户可以同时处理Parent-1和Parent-2(例如,在更新订单#456时创建订单#123)并独立地进行更改。

  8. “工作单元管理器”创建具有嵌入式EntityManagers(EM)的UoW。它使用名为“EntityManagerProvider”的帮助程序服务创建新的EM。 “EntityManagerProvider”负责创建新EM并使用“全局数据”(如参考列表(例如,状态,StatusCodes,颜色等))填充它们。

  9. “EntityManagerProvider”(EMP)在内部使用源MetadataStore和这些静态引用列表的规范版本保持只读“主EM”。它创建的新EM通常是这个隐藏的主EM的副本。因此,整个系统只发出一个元数据请求和一个静态引用列表请求。 EMP负责将源材料分发给它创建的新EM。

  10. 你需要多少钱?我不知道。

答案 1 :(得分:2)

我的好友Steve Schmidt提供了另一种必须考虑的攻击途径:一个ViewModel / Multiple Views

在这种方法中,您可以组合不同的视图,每个视图专用于根实体及其子对象图的特定视角。这些与原始提案的视图大致相同(如果不相同)。

有什么不同?只有一个ViewModel!使用Durandal的“ko compose”自定义Knockout绑定可以轻松地将多个视图附加到同一个ViewModel。您可以在布局级别获得良好的分离,而无需站立并行VM。

当提供模型的非可视化表示逻辑很简单但您想从不同的角度(AKA,“围绕数据旋转”)查看该模型时,这种方法很有效。

Master / Detail可以是一个简单的例子。想象一下参考列表编辑器,其中列表项位于顶部的“网格”中,右侧的简单表单显示所选项目的详细信息。这里没有太多逻辑。为什么要打扰两个虚拟机并且必须将“列表虚拟机”中的项目列表与“编辑器虚拟机”中的所选项目进行协调?作为单个VM更简单。

在简单的情况下,这非常好用,在典型的应用程序中有很多简单的情况。

将此视为我在其他答案中描述的重型多VM技术的补充。