【问题标题】:Recursion in an ASP.NET MVC viewASP.NET MVC 视图中的递归
【发布时间】:2010-10-30 20:38:22
【问题描述】:

我有一个用于类别内一组项目的嵌套数据对象。每个类别都可以包含子类别,并且子类别的深度没有限制。 (文件系统会有类似的结构。)它看起来像这样:

class category
{
    public int id;
    public string name;
    public IQueryable<category> categories;
    public IQueryable<item> items;
}
class item
{
    public int id;
    public string name;
}

我将一个类别列表作为IQueryable&lt;category&gt; 传递给我的视图。我想将类别输出为一组嵌套的无序列表 (&lt;ul&gt;) 块。我可以嵌套 foreach 循环,但是子类别的深度将受到嵌套 foreach 块的数量的限制。在 WinForms 中,我使用递归完成了类似的处理来填充 TreeView,但我还没有看到任何在 ASPX MVC 视图中使用递归的示例。

可以在 ASPX 视图中进行递归吗?是否还有其他包含递归视图输出的视图引擎?

【问题讨论】:

  • 当我写这个问题时,我想我没有理解IQueryableIEnumerable 之间的区别。我现在使用IEnumerable,因为视图没有进行任何查询,应该只枚举数据。

标签: asp.net-mvc recursion


【解决方案1】:

像这样创建自己的 HtmlHelper 扩展方法:

namespace System.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        public static string CategoryTree(this HtmlHelper html, IEnumerable<Category> categories)
        {
            string htmlOutput = string.Empty;

            if (categories.Count() > 0)
            {
                htmlOutput += "<ul>";
                foreach (Category category in Categories)
                {
                    htmlOutput += "<li>";
                    htmlOutput += category.Name;
                    htmlOutput += html.CategoryTree(category.Categories);
                    htmlOutput += "</li>";
                }
                htmlOutput += "</ul>";
            }

            return htmlOutput;
        }
    }
}

你应该问这个问题很有趣,因为我实际上是在昨天创建了其中一个。

【讨论】:

  • 我喜欢这段代码。编写这样一个专门的辅助函数似乎很奇怪,但这会比托马斯回答中的递归局部视图更好吗?
  • 我 99% 肯定这里的性能会更好。 'RenderPartial' 有一些开销,而作为一个简单的辅助方法,开销尽可能小。
  • 这也是一个很好的解决方案 - 但是,我会使用 TagBuilder 类来生成 HTML。或者至少是一个 StringBuilder,而不是仅仅连接字符串... ;)
  • 这种方法的问题是所有的html都在一行上。如果您尝试有多行,则标识会出错。因此,PartialView 更适合这种数据量。我认为 HtmlHelper 扩展方法应该只用于单个标签,就像它们在框架中使用的一样。
  • 将所有 html 放在一行有什么问题?浏览器不关心缩进。除了 IE6 之外,如果您对 IE6 + LI 标签 + CSS 有所了解,最好将它们全部放在一起。但是,如果您对此感到担心,请添加一些换行符等。您可能想阅读 Rob Conery 的“避免标签汤”博客文章,他遵循“如果有 IF,就让它成为帮手”的规则。此外,MSDN 文章“ASP.NET MVC 应用程序的指导原则”,因为它有一个关于 HTML 助手的好部分。
【解决方案2】:

您可以使用辅助方法。

@model Models.CategoryModel

@helper TreeView(List<Models.CategoryModel> categoryTree)
{
    foreach (var item in categoryTree)
    {
    <li>
        @if (item.HasChild)
        {
            <span>@item.CategoryName</span>
            <ul>
                @TreeView(item.ChildCategories)
            </ul>
        }
        else
        {
            <span class="leaf @item.CategoryTreeNodeType.ToString()" id="@item._CategoryId">@item.CategoryName</span> 
        }
    </li>
    }
}

<ul id="categorytree">
    <li>@Model.CategoryName
    @TreeView(Model.ChildCategories)
    </li>
</ul>

更多信息可以在这个链接上找到: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

【讨论】:

  • +1 是一种有趣的替代方法。我还没有看到。
【解决方案3】:

您可以使用 lambdas 重复使用 html 部分

例子


public class Category
    {
        public int id;
        public string name;
        public IEnumerable categories;
    }
 <%
        Action<IEnumerable<Category>> categoriesMacros = null;
        categoriesMacros = categories => { %>
        <ul>
            <% foreach(var c in categories) { %>
                <li> <%= Html.Encode(c.name)%> </li>
                <% if (c.categories != null && c.categories.Count() > 0) categoriesMacros(c.categories);  %>
            <% } %>
        </ul>
        <% }; %>

    <% var categpries = (IEnumerable<Category>)ViewData["categories"]; %>
    <% categoriesMacros(categpries); %>

【讨论】:

  • 这是一个更好的解决方案。深思熟虑
【解决方案4】:

您可以通过将每个&lt;ul&gt; 列表放在PartialView 中轻松地做到这一点,对于您需要开始的每个新列表,您只需调用Html.RenderPartial("myPartialName");

所以Category PartialView 可能看起来像这样:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Category>>" %>
<% foreach(Category cat in ViewData.Model) { %>
     <li><p><%= cat.name %></p>
        <% if (cat.categories.Count > 0) {
                Html.RenderPartial("Category", cat.Categories);
           } %></li>
<% } %>

在您的视图中,您只需发送“根”集合作为局部视图的模型:

<% Html.RenderPartial("Category", ViewData.Model) %>

编辑:

  • 我忘记了 Html.RenderPartial() 调用的第二个参数 - 当然必须将类别作为模型传递。
  • 当然,您对我犯的 DRY 错误是正确的 - 我已相应地更新了我的代码。

【讨论】:

  • 局部视图内部,如何将cat.categories设置为子类别局部视图的模型?
  • RenderPartial 方法可以使用第二个参数作为它的模型。就我个人而言,我不会在您的页面上进行循环,只需将其传递给您的类别集合并在那里开始循环 - 这样会更干燥。
猜你喜欢
  • 2015-04-03
  • 2013-07-07
  • 1970-01-01
  • 2016-12-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 2011-11-08
相关资源
最近更新 更多