Problem when adding a toolbar and updating TreeGrid

The problem is that when I add Toolbar to TreeGrid I start getting an error

System.NullReferenceException: Object reference not set to an instance of an object.
   at Syncfusion.Blazor.TreeGrid.Internal.GridRenderer`1.DataBoundHandler()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Syncfusion.Blazor.Grids.SfGrid`1.OnAfterScriptRendered()
   at Syncfusion.Blazor.SfBaseComponent.OnAfterRenderAsync(Boolean firstRender)
   at Syncfusion.Blazor.SfDataBoundComponent.OnAfterRenderAsync(Boolean firstRender)
   at Syncfusion.Blazor.Grids.SfGrid`1.OnAfterRenderAsync(Boolean firstRender)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)


It happens exactly when you add the Toolbar

Another 1 problem is that when I change a value in DropDown and then go back to the previous value I have duplicate child elements


Here's my page code:

@page "/subdivisions"


@using RTI.CompaniesManagementPortal.Client.Adaptor
@using RTI.CompaniesManagementPortal.Client.Components.Models
@using Syncfusion.Blazor
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.TreeGrid
@using Syncfusion.Blazor.Data
@using Syncfusion.Blazor.DropDowns
@using Action = Syncfusion.Blazor.Grids.Action
@inherits RTI.CompaniesManagementPortal.Client.Components.Base.BasePage
@inject CompanyApiClient CompanyApiClient


<PageTitle>@Localizer["Subdivisions"]</PageTitle>


<SfDropDownList TValue="string" Placeholder="@Localizer["Choose Company"]" TItem="CompanyDTO" @bind-Value="DropVal" DataSource="@Companies" Width="300px">
    <DropDownListFieldSettings Value="companyCode" Text="companyName"></DropDownListFieldSettings>
    <DropDownListEvents TItem="CompanyDTO" TValue="string" ValueChange="OnDropDownValueChange"></DropDownListEvents>
</SfDropDownList>


<SfTreeGrid ID="SubdivisionsTreeGrid"
            TValue="SubdivisionTreeModel"
            IdMapping="Id"
            ParentIdMapping="ParentId"
            HasChildMapping="IsParent"
            TreeColumnIndex="0"
            Toolbar="@(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" })"
            Query=@Query
            AllowPaging="true"
            AllowSorting="true"
            AllowFiltering="true">
    @* <TreeGridEditSettings AllowAdding="true"/> *@
    <TreeGridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true"
                          Mode="Syncfusion.Blazor.TreeGrid.EditMode.Row"
                          NewRowPosition="RowPosition.Below">
    </TreeGridEditSettings>
    <TreeGridPageSettings PageSize="3"></TreeGridPageSettings>
    <TreeGridFilterSettings Type="Syncfusion.Blazor.TreeGrid.FilterType.FilterBar"></TreeGridFilterSettings>
    <SfDataManager AdaptorInstance="@typeof(SubdivisionsAdaptor)" Adaptor="Adaptors.CustomAdaptor" @ref="dataManager"></SfDataManager>
    <TreeGridColumns>
        <TreeGridColumn Field="Id" IsPrimaryKey="true" HeaderText="@Localizer["SubdivisionId"]">
            <Template Context="data">
                @{
                    var subdivisionModel = data as SubdivisionTreeModel;
                }
                @FormatDefault(subdivisionModel.Id)
            </Template>
        </TreeGridColumn>
        <TreeGridColumn Field="ChildId" AllowEditing="false" HeaderText="@Localizer["TradePointId"]">
            <Template Context="data">
                @{
                    var subdivisionModel = data as SubdivisionTreeModel;
                }
                @FormatDefault(subdivisionModel.ChildId)
            </Template>
        </TreeGridColumn>
        <TreeGridColumn Field="Name" HeaderText="@Localizer["Name"]">
            <Template Context="data">
                @{
                    var subdivisionModel = data as SubdivisionTreeModel;
                }
                @FormatDefault(subdivisionModel.Name)
            </Template>
        </TreeGridColumn>
        <TreeGridColumn Field="Code" HeaderText="@Localizer["Code"]">
            <Template Context="data">
                @{
                    var subdivisionModel = data as SubdivisionTreeModel;
                }
                @FormatDefault(subdivisionModel.Code)
            </Template>
        </TreeGridColumn>
        <TreeGridColumn Field="Num" HeaderText="@Localizer["Number"]">
            <Template Context="data">
                @{
                    var subdivisionModel = data as SubdivisionTreeModel;
                }
                @FormatDefault(subdivisionModel.Num)
            </Template>
        </TreeGridColumn>
        <TreeGridColumn HeaderText="@Localizer["Actions"]" Width="200">
            <Template>
                @{
                    var subdivision = (context as SubdivisionTreeModel);
                }
                @if (subdivision.IsParent)
                {
                    <button class="btn btn-primary btn-sm" @onclick="() => AddSubdivisionPoint(subdivision.Id)">
                        @Localizer["AddTradePoint"]
                    </button>
                }
            </Template>
        </TreeGridColumn>
    </TreeGridColumns>
</SfTreeGrid>


@code {
    private SfDataManager dataManager;
    public string DropVal = "RTI001";
    private Query Query = new Query();
    // private List<string> ToolbarItems = new List<string>() { "Add", "Cancel" };


    List<CompanyDTO> Companies;


    protected override async Task OnInitializedAsync()
    {
        Query = new Query().AddParams("companyCode", DropVal);
        await LoadCompanies();
    }


    private async Task LoadCompanies()
    {
        // TODO: На стороне Backend реализовать свойство getAllRows или новый endpoint для получения всех строк
        var request = await CompanyApiClient.GetPaginatedCompanies(0, 9999);
        Companies = request.PageItems;
    }


    private async Task OnDropDownValueChange(ChangeEventArgs<string, CompanyDTO> args)
    {
        Query = new Query().AddParams("companyCode", args.ItemData.companyCode);
    }


    private async Task AddSubdivisionPoint(long subdivision)
    {
    }


    private void ActionBeginHandler(ActionEventArgs<SubdivisionTreeModel> obj)
    {
        if (obj.RequestType == Action.Refresh)
        {
            Query = Query.AddParams("companyCode", DropVal);
        }
    }


    private string FormatDefault<T>(T value)
    {
        if (EqualityComparer<T>.Default.Equals(value, default(T)))
        {
            return "";
        }
        return value?.ToString();
    }
}

Custom Adaptor's code:

using RTI.CompaniesManagementPortal.Client.Components.Models;
using Syncfusion.Blazor.Data;
using Syncfusion.Blazor;


namespace RTI.CompaniesManagementPortal.Client.Adaptor
{
    public class SubdivisionsAdaptor : DataAdaptor
    {
        private readonly CompanyApiClient _apiClient;
        private readonly IMapper _mapper;


        public SubdivisionsAdaptor(CompanyApiClient apiClient, IMapper mapper)
        {
            _apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
            _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
        }


        public override async Task<object> ReadAsync(DataManagerRequest dm, string key = null)
        {
            var companyCode = string.Empty;
            if (dm.Params is not null && dm.Params["companyCode"] is not null)
            {
                companyCode = dm.Params["companyCode"]?.ToString();
            }
            var request = await _apiClient.GetCompanySubdivisionsByCompanyCode(companyCode);
            var subdivisionsDto = request.ResponseObject;
            var treeData = new List<SubdivisionTreeModel>();
            treeData.AddRange(_mapper.Map<List<SubdivisionTreeModel>>(subdivisionsDto));
            if (subdivisionsDto != null)
            {
                var tasks = subdivisionsDto.Select(async subdivision =>
                {
                    try
                    {
                        // TODO: Implement normal endpoint
                        var pointsRequest = await _apiClient.GetPaginatedTradePoints(subdivision.id, 0, 999);
                        var subDivisionPoints = pointsRequest.ResponseObject;
                        var mappedSubdivisions = _mapper.Map<List<SubdivisionTreeModel>>(subDivisionPoints.PageItems);
                        if (mappedSubdivisions != null)
                        {
                            treeData.AddRange(mappedSubdivisions);
                        }
                    }
                    catch (Exception e)
                    {
                        throw;
                    }
                });


                await Task.WhenAll(tasks);
            }


            IEnumerable<SubdivisionTreeModel> DataSource = treeData ?? new();


            if (dm.Search != null && dm.Search.Count > 0)
            {
                // Searching
                DataSource = DataOperations.PerformSearching(DataSource, dm.Search);
            }
            if (dm.Sorted != null && dm.Sorted.Count > 0)
            {
                // Sorting
                DataSource = DataOperations.PerformSorting(DataSource, dm.Sorted);
            }
            if (dm.Where != null && dm.Where.Count > 0)
            {
                // Filtering
                DataSource = DataOperations.PerformFiltering(DataSource, dm.Where, dm.Where[0].Operator);
            }
            int count = DataSource.Count();
            DataResult DataObject = new DataResult();
            if (dm.Aggregates != null) // Aggregation
            {
                DataObject.Result = DataSource;
                DataObject.Count = count;
                DataObject.Aggregates = DataUtil.PerformAggregation(DataSource, dm.Aggregates);


                return dm.RequiresCounts ? DataObject : (object)DataSource;
            }
            return dm.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource;
        }


        public override Task<object> InsertAsync(DataManager dataManager, object record, string additionalParam)
        {
            return base.InsertAsync(dataManager, record, additionalParam);
        }


        public override Task<object> UpdateAsync(DataManager dataManager, object record, string primaryColumnName, string additionalParam)
        {
            return base.UpdateAsync(dataManager, record, primaryColumnName, additionalParam);
        }
    }
}



2 Replies 1 reply marked as answer

RD RTi Developer February 13, 2025 01:22 PM UTC

If you encounter the same problem, upgrade to the latest version syncfusion blazor.


I had this problem on version: 26.2.14, upgrading to 28.2.5 solved everything


Marked as answer

SS Shereen Shajahan Syncfusion Team February 14, 2025 05:23 AM UTC

Hi RTi Developer,

Glad that the issue has been resolved. Please get back to us for assistance in the future.

Regards,

Shereen


Loader.
Up arrow icon