Search code examples
recursionnestedblazor

Recursive markup in Blazor


I have a LogModel that has a Children property of type LogModel, so a recursive/nested structure. There is also a Collapsed bool property and a Description string.

I am trying to display this in Blazor server side markup, but cant figure out how to process the recursive piece.

I want it to show the Description, with a +/- button which has @onclick that toggles the Collapse property, which then controls the collapse class in bootstrap, collapsing/hiding all children from display.
Like this example: How to collapse/expand Razor components using Blazor syntax?

My 2nd idea was to do the recursive piece inside Code, which returns a MarkupString. This displayed ok, but then I couldnt work out how to bind the +/- button to an event to toggle the collapsed property, because the @onlick cant be done from inside MarkupString from what I've read.

Thanks in advance, Aaron.


Solution

  • Here's a solution based on the information you've given. It's not pretty: you'll need to sort the Bootstrap CSS out to suite your needs.

    LogModel.cs

    using System.Collections.Generic;
    
    namespace StackOverflow.Answers
    {
        public class LogModel
        {
            public string Description { get; set; }
    
            public List<LogModel> Children { get; set; } = new List<LogModel>();
    
        }
    }
    

    LogDisplayControl.razor

    @namespace StackOverflow.Answers
    <div class="container m-0 p-1 border border-secondary">
        <div class="row">
            <div class="col-11">
                @this.Model.Description
            </div>
            @if (this.hasChildren)
            {
                <div class="col-1">
                    <button class="btn @this.buttonCss" type="button" @onclick="ToggleShow">
                        @buttonText
                    </button>
                </div>
            }
        </div>
        @if (this.Show)
        {
            <div class="row">
                <div class="col-12">
                    @if (this.hasChildren)
                    {
                        @foreach (var child in this.Model.Children)
                        {
                            <LogDisplayControl Model="child"></LogDisplayControl>
                        }
                    }
                </div>
            </div>
        }
    </div>
    
    @code {
    
        [Parameter] public LogModel Model { get; set; }
    
        private bool Show;
    
        private void ToggleShow()
            => this.Show = !this.Show;
    
        private bool hasChildren => this.Model?.Children?.Count > 0;
    
        private string buttonText => this.Show ? "Hide" : "Show";
    
        private string buttonCss => this.Show ? "btn-dark" : "btn-primary";
    
        private string HeaderCols => this.hasChildren ? "col-11" : "col-12";
    
    }
    

    Demo Page

    @page "/Accordion"
    @foreach (var child in Model)
    {
        <LogDisplayControl Model="child" />
    }
    
    @code {
    
        private List<LogModel> Model = new List<LogModel>();
    
        protected override void OnInitialized()
        {
            var model1_1_1 = new LogModel { Description = "Log Model 1.1.1" };
            var model1_1_2 = new LogModel { Description = "Log Model 1.1.2" };
            var model1_1 = new LogModel { Description = "Log Model 1.1", Children = new List<LogModel> { model1_1_1, model1_1_2 } };
    
            var model1_2 = new LogModel { Description = "Log Model 1.2" };
    
            var model1 = new LogModel { Description = "Log Model 1", Children = new List<LogModel> { model1_1, model1_2 } };
    
            var model2_1_1 = new LogModel { Description = "Log Model 2.1.1" };
    
            var model2_1 = new LogModel { Description = "Log Model 2.1", Children = new List<LogModel> { model2_1_1 } };
    
            var model2 = new LogModel { Description = "Log Model 2", Children = new List<LogModel> { model2_1 } };
    
            Model.Add(model1);
            Model.Add(model2);
        }
    }