ASP.NET MVC中的UI组合

时间:2009-01-17 09:13:08

标签: asp.net-mvc composition

您将如何在ASP.NET MVC视图中支持外部可组合部分?

这是什么意思?想想“每个页面上的登录框”或“iGoogle”。这些东西需要在每个控制器/视图外部的某些位置。

这样做的一种方法是在视图中添加组件,如下所示:

<% foreach (var component in GetComponents()) {%>
    <%= Html.RenderPartial(component.ViewName, component.ViewData)%>
<%} %>

在上面的示例中,我正在寻找一种方法,让每个组件的控制器提供viewname和viewdata,而不是显示它们的视图的控制器。您可以建议的任何完全不同的解决方案也会引起关注。过滤器,Web表格等

更新:我会尝试解释一下我试图用一个例子来解决的问题。我会选择登录功能。

在典型的webforms应用程序中,这可能是一个用户控件,它在页面生命周期的load事件中检索适当的数据并更新一些UI控件。点击后页面会回发,我们可以在同一用户控件中的点击事件中对发布的信息采取行动。

根据我目前对ASP.NET MVC风格的理解,首先接受请求的控制器将检索适当的数据并将其传递给视图将它传递给登录部分视图。登录视图将定义一个表单,其后置操作指向登录控制器的登录操作。登录操作使用发布的信息,我们可以选择使用一些漂亮的方案将请求传递给原始控制器。

我认为有一种比上述更聪明的方法,不需要我将控制器逻辑放在视图/母版页中。

4 个答案:

答案 0 :(得分:5)

有两种方法可以做到这一点:

一:创建一个BaseController,始终获取这些组件所需的数据,设置ViewData [“loginbox”] = loginBoxData,然后你可以像这样传递它。

<% foreach (var component in GetComponents()) {%>
        <%= Html.RenderPartial(component.ViewName, ViewData[component.Name])%>
<%} %>

关于这种方法的问题在于你需要有逻辑来说明何时将获取组件所需的所有这些数据,何时不去。

二:

您可以从here下载MVC Futures DLL。

你引用它,并记得将其命名空间添加到你的web.config中,然后你应该能够说:Html.RenderAction("blah") - 这个方法通过联系控制器,启动视图和返回HTML。

答案 1 :(得分:2)

您可以使用the master page and use content placeholders查看内容页面并查看用户控件。

ASP.NET MVC Framework Beta中的默认项目模板具有View Content Page的内容占位符。页面右上角的登录/注销链接还有一个View User Control。

查看内容页面

视图内容页面应该与您正在查看的视图中使用的数据一起使用。即如果您正在浏览Index中的HomeController操作,则服务器将呈现Home/Index.aspx视图内容页。

在母版页中,您需要以下一行:

<asp:ContentPlaceHolder ID="Holder1" runat="server" />

在视图内容页面中,您可以使用以下内容为该占位符撰写:

<asp:Content ID="Content1" ContentPlaceHolderID="Holder1" runat="server">
  <p>This text goes to "Holder1" content place holder and
     I can access the ViewData.</p>
  <p><%= ViewData["Message"] %>
</asp:Content>

每个asp:ContentPlaceHolder通过ID对应于视图的asp:Content。因此,如果您想要多个占位符,请执行以下操作:

<asp:ContentPlaceHolder ID="Holder1" runat="server" />
<asp:ContentPlaceHolder ID="Holder2" runat="server" />

...您可以在视图内容页面中处理它们,如下所示:

<asp:Content ID="Content1" ContentPlaceHolderID="Holder1" runat="server">
  <p>This text goes to "Holder1" content place holder</p>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Holder2" runat="server">
  <p>This text goes to "Holder2" content place holder</p>
</asp:Content>

查看用户控件

无论您正在查看哪个视图,都可以使用查看用户控件。以下是查看用户控件的一些示例,取自Wekeroad's blog post about View User Controls。要使用视图用户控件:

<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”)%>

如果您有需要,还可以将数据和匿名类型传递给它。

<%=Html.RenderUserControl(“~/UserControls/UserList.ascx”,ViewData.Users, new {GroupID=2})%>

答案 2 :(得分:1)

没有正确理解问题,我建议使用母版方法。即。让母版页呈现从母版页派生的组件和页面。

但是,我担心这不是一个好的答案。我不明白这一点:“..每个组件的控制器都提供了viewname和viewdata,而不是它们显示的视图的控制器。”

答案 3 :(得分:0)

这是一个古老的问题,但这是我对剃须刀的看法:

在CQRS样式的应用程序中,您可以将ICommand推送到单个服务端点,服务会找出如何路由它。

这是一个很好的例子:https://github.com/ncqrs/ncqrs/blob/master/Framework/src/Ncqrs/Commanding/ServiceModel/CommandService.cs

说什么 - 我们试图执行查询而不是尝试执行命令?

而不是1,我们传递了1个或更多作为IViewModelExecutor的列表?

在您的主页面中,您有一个主ViewModel,它有每个子ViewModel的定义,当页面加载时,我们要求服务获取与此视图相关的所有视图模型:

service.Load(new List<IViewModelExecutor>{UserinfoViewModel, UserAccountViewModel });

服务从readmodel加载数据并在需要的地方加载缓存。子视图模型使用Html.RenderPartial("subviewname", Model.SubViewModel)

传递给子视图

子视图知道如何根据模型渲染自身。

现在 - 因为我们有一个带有ajax更新的组合UI - 假设我们想要更新页面的UserAccount部分。在控制器中,我们有一个动作负责渲染那个部分。

它也可以从service.Load(new List<IViewModelExecutor>{UserAccountViewModel })

请求

并且只返回它需要的视图模型的一部分 - 可能是缓存的。除非params改变了obv。

现在我们有一个可以全部或部分构建的组合UI。

使用内容协商,我们可以根据请求中的HTTP标头提供HTML片段,JSON或XML。