83

Okay, so I want to display a list containing lists of lists of lists...

I have no way of knowing how many levels there are to display, so I figured this is where I break out the old recursive routine.

I'm having trouble with exactly how to go about this though.

This is what I have so far (in view - simplified):

@foreach(MyObject item in @Model.ListOfObjects){ 
    <div> @item.Title </div>
    //Call recursive function?
}

Now each of these objects also have a List<MyObject>. I want to display each level below this div, with a tab indent per level for instance.

I was thinking a Razor function would be the thing to do here, but I need some help in forming it. Here's my thinking:

@functions{
    public static void ShowSubItems(MyObject _object){
         if(_object.ListOfObjects.Count>0){
             foreach(MyObject subItem in _object.listOfObjects){

                 // Show subItem in HTML
                 ShowSubItems(subItem);
             }
         }
     }
 }

But as you can see, I plainly need some help :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dynde
  • 2,592
  • 4
  • 33
  • 56

3 Answers3

218

The Razor view engine allows to write inline recursive helpers with the @helper keyword.

@helper ShowTree(IEnumerable<Foo> foos)
{
    <ul>
        @foreach (var foo in foos)
        {
            <li>
                @foo.Title
                @if (foo.Children.Any())
                {
                    @ShowTree(foo.Children)
                }
            </li>
        }
    </ul>
}
Paolo Moretti
  • 54,162
  • 23
  • 101
  • 92
  • 5
    one thing is remaining that it must call on view by **@ShowTree(Foos)** – vicky Mar 13 '15 at 13:02
  • 1
    This works really well. Also may make sense to mention http://stackoverflow.com/questions/12943245/access-global-page-variable-in-helper for referencing local variables on the cshtml page. – Travis J Mar 07 '17 at 23:45
  • 2
    how can be implemented this code in asp.net core 2? in asp.net core 2, there is no @helper – x19 Jan 10 '18 at 21:28
  • 2
    @Jahan Looks like `@helper`s are gone in ASP.NET Core. Inline functions are one potential alternative: https://github.com/aspnet/Razor/issues/715#issuecomment-272890766 but look much less readable! – Paolo Moretti Jan 10 '18 at 23:22
  • @PaoloMoretti When I define it as recursive Func, how can I do it? I want to create a nested comments with recursive Func. This error shows in VS2018 IDE: "Use of unassigned local variable 'FunctionName' local variable 'FunctionName' might not be initialized before accessing" – x19 Jan 11 '18 at 13:20
  • @PaoloMoretti Please visit this question: https://stackoverflow.com/q/48211043/1817640 – x19 Jan 11 '18 at 15:55
13

I think it is best to create an HTML helper for this. Something like this:

public static string ShowSubItems(this HtmlHelper helper, MyObject _object)
{
     StringBuilder output = new StringBuilder();
     if(_object.ListOfObjects.Count > 0)
     {
         output.Append("<ul>");

         foreach(MyObject subItem in _object.listOfObjects)
         {
             output.Append("<li>");
             output.Append(_object.Title);
             output.Append(html.ShowSubItems(subItem.listOfObjects);
             output.Append("</li>")
         }
         output.Append("</ul>");
     }
     return output.ToString();
}

Then call it like this:

@foreach(MyObject item in @Model.ListOfObjects){
    <div> @item.Title </div>
    @html.ShowSubItems(item)
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
3

In .Net core Instead of helper tag we have to use @functions. This below code might useful to some people. Here "IssuerData" is my custom object

@{
      ShowTreeData(Model.HierarchyList);
}

    @functions{

  public static void ShowTreeData(IList<IssuerData> issuers)
      {
       <ul>
      @foreach (var i in issuers)
           {
           <li>
            @i.IssuerName
             @if (i.Children.Any())
               {
                 ShowTreeData(i.Children);
               }
            </li>
          }
       </ul>
       }
   }