Hello everyone,
I've added a TreeGrid in my Blazor server app that retrieves parent and child folders from our API and displays them in a hierarchy.
I only load the top-level parent folders when the app initially loads because there are many folders. To prevent performance issues, child folders are loaded when a top-level folder is expanded. It works as expected, except the TreeGrid does not update itself, so I have to force a DataSource refresh in the OnRowExpanded method.
Now that I have added selection and search/filter options to the TreeGrid, I am experiencing another problem. I lose my selections when I select items and then filter the grid. In order to keep track of the selected items, I had to create a new list.
I would like to update the SelectedItems list when a TreeGrid row is selected or un-selected. Thanks for your help!
Here is my code:
<h3>Folder Search Demo</h3>
<SfTreeGrid @ref="TreeGrid" DataSource="@Folders" IdMapping="Id" ParentIdMapping="ParentId" TreeColumnIndex="2" AllowFiltering="true" HasChildMapping="HasChildren" ExpandStateMapping="Expanded" Width="500px">
<TreeGridEvents TValue="Folder" Expanded="@OnRowExpanded" RowSelected="@OnRowSelected"></TreeGridEvents>
<TreeGridFilterSettings HierarchyMode="FilterHierarchyMode.Both" Type="Syncfusion.Blazor.TreeGrid.FilterType.Excel"></TreeGridFilterSettings>
<TreeGridSelectionSettings Type="Syncfusion.Blazor.Grids.SelectionType.Multiple"></TreeGridSelectionSettings>
<TreeGridColumns>
<TreeGridColumn Type="Syncfusion.Blazor.Grids.ColumnType.CheckBox" Width="5"></TreeGridColumn>
<TreeGridColumn Visible="false" Field="Id" HeaderText="Folder ID" Width="5" ShowCheckbox="true" IsPrimaryKey="true" IsIdentity="true"></TreeGridColumn>
<TreeGridColumn Field="Name" HeaderText="Folder" Width="30"></TreeGridColumn>
</TreeGridColumns>
</SfTreeGrid>
@code {
SfTreeGrid<Folder> TreeGrid;
public string ErrorMessage { get; set; }
public class Folder
{
public string Id { get; set; }
public string ParentId { get; set; }
public string ParentFolderName { get; set; }
public string Name { get; set; }
public bool Expanded { get; set; }
public bool HasChildren { get; set; }
public bool ChildrenLoaded { get; set; } = false;
public bool isChecked { get; set; }
}
List<Folder> Folders = new List<Folder>();
// List to track selected folders
public List<Folder> SelectedFolders { get; set; } = new List<Folder>();
protected override async Task OnInitializedAsync()
{
await LoadTopLevelFolder();
}
// Get top level parent records (folders) for initial grid rendering
private async Task LoadTopLevelFolder()
{
try
{
VaultApi vaultApi = new VaultApi(vvSecrets);
// Call the API
var topLevelFolder = vaultApi.Folders.GetFolderByPath("/Employees");
if (topLevelFolder != null)
{
var letterFolders = vaultApi.Folders.GetChildFolders(topLevelFolder.Id);
if (letterFolders != null && letterFolders.Any())
{
foreach (var letterFolder in letterFolders)
{
Folders.Add(new Folder
{
Id = letterFolder.Id.ToString(),
Name = letterFolder.Name,
ParentId = null,
ParentFolderName = "Parent",
ChildrenLoaded = false,
Expanded = false,
HasChildren = true
});
}
}
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
}
// Get child records (folders) and adding them to the folder list hierarchy // Debugging statements included to track actions in blazor console for dev purposes only private async Task LoadChildren(Folder folder)
{
try
{
VaultApi vaultApi = new VaultApi(vvSecrets);
Console.WriteLine($"Sending API request for Folder ID: {folder.Id}");
var childFolders = vaultApi.Folders.GetChildFolders(Guid.Parse(folder.Id));
Console.WriteLine($"API response for Folder ID {folder.Id}: {JsonConvert.SerializeObject(childFolders)}");
Console.WriteLine($"Number of child folders in API response: {childFolders?.Count()}");
if (childFolders != null && childFolders.Any())
{
// Check if the parent folder's name consists only of alphabet letters A-Z
var isParentAlphabetic = Regex.IsMatch(folder.Name, @"^[A-Za-z]+$");
foreach (var childFolder in childFolders)
{
// If the parent folder's name is only alphabetic, set HasChildren to false for child folders
var hasChildren = isParentAlphabetic ? false : true;
Folders.Add(new Folder
{
Id = childFolder.Id.ToString(),
Name = childFolder.Name,
ParentId = folder.Id,
ParentFolderName = folder.Name,
Expanded = false,
HasChildren = hasChildren
});
}
folder.HasChildren = true;
folder.Expanded = true;
}
else
{
folder.HasChildren = false;
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
}
// Loads child folders when the parent folder row is expanded
private async Task OnRowExpanded(RowExpandedEventArgs<Folder> args)
{
var folder = args.Data; // The folder record of the expanded row
if (folder.HasChildren && !folder.ChildrenLoaded)
{
await LoadChildren(folder);
folder.ChildrenLoaded = true; // Mark true since children loaded after successful loading to prevent duplicate API calls if the child folders have already been loaded.
this.Folders = new List<Folder>(Folders); // Refresh the list to trigger UI update
StateHasChanged();
}
}
// Get selected folder data from row and add it to the SelectedFolders tracking list
public async Task OnRowSelected(RowSelectEventArgs<Folder> args)
{
SelectedFolders.Add(args.Data);
StateHasChanged();
}
Hi Patrick,
Please refer to the response for your queries.
Query 1 : Can the grid be automatically refreshed when the child folders are added to the data source?
We can use remote data binding in treegrid to render the children on demand (i.e., on expanding, the children will be rendered). Please refer to the documentation and demo below.
Documentation :
https://blazor.syncfusion.com/documentation/treegrid/data-binding#remote-service-binding
Demo :
https://blazor.syncfusion.com/demos/tree-grid/remote-data?theme=fluent
Query 2 : Is there a way to keep my selections when filtering/searching the TreeGrid? I checked the documentation, but I am struggling a bit.
We can manually select the rows using SelectRowAsync method after sorting and filtering in treegrid by RowSelected event and ActionComplete event. We can add the id of the selected index to a list and on action such as sorting and filtering we can find the index of the id and select the records manually by GetCurrentViewRecords.
Please refer to the following code snippet.
|
<TreeGridEvents OnActionComplete=" ActionComplete" RowSelected="RowSelected" TValue="BusinessObject"></TreeGridEvents>
{ if (args.RequestType == Syncfusion.Blazor.Grids.Action.Sorting || args.RequestType == Syncfusion.Blazor.Grids.Action.Filtering || args.RequestType == Syncfusion.Blazor.Grids.Action.ClearFiltering) { var index = this.Treegrid.GetCurrentViewRecords().FindIndex(a => a.TaskId == this.selectedId[0]); this.Treegrid.SelectRowAsync(index); } }
public void RowSelected(RowSelectEventArgs<BusinessObject> args) { if (this.selectedId != null) { this.selectedId = new List<int>(); this.selectedId.Add(args.Data.TaskId); } }
|
Sample : https://blazorplayground.syncfusion.com/rXVftUiUrdqcAitb
Kindly get back to us for further assistance.
Regards,
Shek
Thank you so much, Shek! Using your example, I was able to resolve both issues in my code. I appreciate your thr
Patrick,
Thanks for your update. Please get back to us if you need any further assistance. We are happy to assist you further.
Regards,
Farveen sulthana T