Render tree structure of unknown depth using Blazor

Viewed 1136

I'm using the following recursive function to try and render out an html accordion. It uses reflection to gather all property names and values of a given object. The object is a data tree structure of unknown depth/children etc.

@* Accordoin HTML Template *@
<div class="accordion">
  <input type="checkbox" id="accordion-1" name="accordion-checkbox" hidden>
   <label class="accordion-header" for="accordion-1">
    <i class="icon icon-arrow-right mr-1"></i>
    Title
   </label>
<div class="accordion-body">
    <!-- Accordions content -->
</div>
</div>

@code { 
public void BuildTree(object obj)
{
    if (obj == null) return;
    Type objType = obj.GetType();
    PropertyInfo[] properties = objType.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
        {
            //Build accordion body
        }
        else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
        {
            //Build accordion header
            IEnumerable enumerable = (IEnumerable)propValue;
            if (enumerable != null)
                foreach (object child in enumerable)
                    BuildTree(child);
        }
        else
        {
            //Build accordion header
            BuildTree(propValue);
        }
    }
}

My question is how would I go about rendering the html in a recursive way?

I have tried using a RenderFragment as follows :

RenderFragment BuildTree(object obj)
{
if (obj == null) return @<p>test</p>;
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
    object propValue = property.GetValue(obj, null);
    if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
    {
       return @<p>Some test markup</p>
    }
    else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
    {
        IEnumerable enumerable = (IEnumerable)propValue;
        if (enumerable != null)
            foreach (object child in enumerable)
                BuildTree(child);
    }
    else
    {
        BuildTree(propValue);
    }
}
return @<p>Done</p>;
}

It builds but gives an error on runtime :

Unhandled exception rendering component: Cannot bind to the target method because its signature is not compatible with that of the delegate type.

I know there must be an idiomatic way to do this in blazor but I can't wrap my head around how recursion would work with render fragments.

1 Answers

My 2 cents: components can be self-nested. For my brain, this is the easiest way to render nested components-- a kind of very physical recursion. I'm at work, so I couldn't finish a working solution for you, but hopefully the following template will be clear enough. If you want all branches to display, then drop the boolean logic completely or initialize ShowChildren to true instead of false;

Nestable.Razor

@if(ChildCollection is not null){
    <div @onclick=ToggleVisibility >
        <u>@DisplayForThisObject</u>
    </div>
     @if(ShowChildren){
         foreach (var Child in ChildCollection){
            <Nestable PassedObject=Child />
         }
     }
}
else {
    @DisplayForThisObject;
}

@code {
    [Parameter]
    public object PassedObject {get; set;}
    IEnumerable<object> ChildCollection {get; set;}
    string DisplayForThisObject {get; set;}
    bool ShowChildren {get; set;} = false;

    protected override void OnInitialized()
    {
        DisplayForThisObject = SomePropertyToString();
        ChildCollection = SomemethodToGetTheChildObjects(PassedObject);
        base.OnInitialized();
    }
    void ToggleVisibility(){
        ShowChildren = !ShowChildren;
    }
}
Related