Recursion in an ASP.NET MVC view

19,552

Solution 1

Create your own HtmlHelper extension method like so:

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;
        }
    }
}

Funny you should ask because I actually created one of these just yesterday.

Solution 2

You could easily do it by having each <ul> list in a PartialView, and for each new list you need to start you just call Html.RenderPartial("myPartialName");.

So the Category PartialView could look like this:

<%@ 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>
<% } %>

In your View, you simply send the "root" collection as the model for the partial view:

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

EDIT:

  • I had forgotten the second parameter to the Html.RenderPartial() call - of course the category has to be passed as the model.
  • Of course you are right about the DRY mistake I made - I have updated my code accordingly.

Solution 3

You can use helper methods.

@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>

More info can be found on this link: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

Solution 4

You can reuse html parts with lambdas

Example


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); %>
Share:
19,552
CoderDennis
Author by

CoderDennis

Dennis cut his coding teeth by teaching himself BASIC on a Texas Instruments TI-99/4A computer in the early 80's. His earliest memory of debugging a program was when he found that his mother had typed the letter O instead of the digit 0 in a hexadecimal string that defined the graphics of a program she copied from the listing in a magazine. After discovering Delphi 1.0 during college, he went to work on a fax broadcast system and other telephony projects. In the late 90's he worked with a few record labels to put software on their music CD's. This included Windows screen savers of album art work and a music player that scrolled the lyrics of each song across the screen -- all written in Delphi. After that, he spent about 5 years doing web development in PHP (even working with a PHP MVC Framework) before discovering ASP.NET and C#. He remained somewhat proud of the fact that he had never worked with Visual Basic until starting a job in 2007 where it was the company's language of choice for developing Microsoft Office customizations for the legal industry. Even though he got lost any time he tried to look at VB6 code, with the advent of LINQ to XML and XML Literals in VB9, he was happy to be a VB.NET developer. In 2012, upon returning to full-time web development, he was pleased to discover that working with JavaScript sure isn't what it used to be. He has been running Elm in production since June 2018 -- no runtime errors, for the win! From 2014 to 2019 he organized the Dallas Functional Programmers user group (dallasfp.com) and enjoyed exploring various FP languages with people who share his passion.

Updated on June 11, 2022

Comments

  • CoderDennis
    CoderDennis almost 2 years

    I have a nested data object for a set of items within categories. Each category can contain sub categories and there is no set limit to the depth of sub categories. (A file system would have a similar structure.) It looks something like this:

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

    I am passing a list of categories to my view as IQueryable<category>. I want to output the categories as a set of nested unordered list (<ul>) blocks. I could nest foreach loops, but then the depth of sub categories would be limited by the number of nested foreach blocks. In WinForms, I have done similar processing using recursion to populate a TreeView, but I haven't seen any examples of using recursion within an ASPX MVC view.

    Can recursion be done within an ASPX view? Are there other view engines that include recursion for view output?