Building a Folder Tree in Blazor

 Since Blazor is a relatively new technology, it may be hard to find new common features that can be found in other frameworks. That is the case of a recursive folder tree, which I could find only in "add my package to your project" solutions. The idea of this article is to share the front-end and the back-end of the solution, the whole code that you need to build a folder tree.

Starting with the Back-end

First of all, I worked with a class called "Group", which will be the "Folder" (you can call it "Folder" if you want). It's a simple and common class. The only noticeable difference is that it has a "ParentGroupId" integer property to reference itself (yes, a group inside a group!), which is important to create the "folder inside a folder" effect that we need.

C#

public class Group
{
    public string Name { get; set; }
    public string? Description { get; set; }
    public int? ParentGroupId { get; set; }
    public Group ParentGroup { get; set; }
    public virtual List<Group> SubGroups { get; set; }
}

You can store it in SQL Server, MySQL, whatever. I will not cover how to use the SQL Server, or create the table. I will assume you can already save that class in a table and retrieve the information.

Recursive Method to Get All Folders/Groups Info

Inside your repository/controller/whatever you are using, you need to create a method to get all groups/folder recursively. In my case, I use a framework called Sparc from my company that does all that magic. In the end, I will post a link where you can get more information. This method is where you will have groups inside groups infinitely (no matter how many groups you have there). You can find the code below.

C#

public async Task<List<Group> GetGroups()
{
    // Get all groups from DB to save all the extra database calls
    var allGroups = await GroupRepository
        .Query
        .ToListAsync();
    // Start recursive function with the top of the tree
    LoadSubgroups(allGroups, null);
    allGroups = allGroups.Where(x => x.ParentGroupId == null).ToList();
    
    return allGroups;
}
private List<Group> LoadSubgroups(List<Group> allGroups, Group? parentGroup)
{
    var groups = allGroups.Where(x => x.ParentGroupId == parentGroup?.Id).ToList();
    foreach (var group in groups)
        group.SubGroups = LoadSubgroups(allGroups, group);
    return groups;
}

You can notice that the GetGroups() method calls the LoadSubgroups() method, and the LoadSubgroups() calls itself! The idea is that the LoadSubgroups() method calls itself recursively until there are groups to load.

Working on the Front-End With Components

We will have the same challenge we had on the backend: recursiveness! In this case, we need to use Blazor Components to create the recursive effect to create new HTML elements for each new Group/Folder that will be shown. I will assume that you can already get the Group/Folder information from the API.

GroupTree Component

First things first: we will need to create the GroupTree/FolderTree component:

C#

<div class="folder-tree-wrapper">
    <ul class="folder-tree">
        @foreach (var group in Groups)
        {
            <GroupItem Group="group" />
        }
    </ul>
</div>
@code {
    [Parameter] public List<GetGroupsResponse> Groups { get; set; }
}

You can notice that there is an HTML element that is not defined: the <GroupItem /> element! That will be another component (the child component) that will actually have the groups.

GroupItem Component

C#

<li>
    <div class="item-container">
        @if (Group.SubGroups.Any())
            {
                @if (Group.IsExpanded)
                {
                    <i class="material-icons expand-more" 
                     style="transform: rotate(180deg)" 
                     @onclick="ToggleGroup">expand_less</i>
                }
                else
                {
                    <i class="material-icons expand-more" 
                     style="transform: rotate(90deg)" 
                     @onclick="ToggleGroup">expand_less</i>
                }
            }
        <i class="material-icons" style="cursor: pointer; 
                 @(Group.SubGroups.Any() ? "" : "padding-left: 18px")">
                 @(allEntriesSelected ? "check_box" : "check_box_outline_blank")</i>
        <div style="display: inline-block" @onclick="ToggleGroup">
            <i class="material-icons">folder</i>
            <div style="display: inline-block">@Group.Name</div>
        </div>
    </div>
    @if (Group.SubGroups.Any() && Group.IsExpanded)
    {
        <ul>
        @foreach (var subGroup in Group.SubGroups)
        {
            <GroupItem Group="subGroup"/>
        }
        </ul>
    }
</li>
@code {
    [Parameter] public GetGroupsResponse Group { get; set; }
    public bool allEntriesSelected { get; set; }
    async Task ToggleGroup()
    {
        Group.IsExpanded = !Group.IsExpanded;
    }
}

And that is it! That's all you need to create a Folder/Tree solution with Blazor. The only thing you need to do now is to call the GroupTree component on the page you want to use it, like this:

C#

<GroupTree Groups="groups" />

Source: CodeProject

Post a Comment

0 Comments