use Query on SfTreeGrid with SfdataManager

Hello,

I'm using  property query with SfDataManager on TreeGrid,

The first time, TreeGrid displays all data and 1 seconde after, is refreshed with query data, for second time and after, razor page is blocked, traces give us informations (below)

Moreover data are expanded whereas property ExpandStateMapping="false"

It's works good for all when i use data from datasource property


TreeGrid :

<SfTreeGrid TValue=TraceabilityResult @ref="_traceabilityResultListGrid" Toolbar="_toolbar" AllowSorting="true" AllowFiltering="true" AllowPaging="true" AllowResizing="true"

AllowSelection="false" EnableHover="false" AllowExcelExport="true" AllowPdfExport="true" Height="100%" Width="100%"

IdMapping="@nameof(TraceabilityResult.Id)" ParentIdMapping="@nameof(TraceabilityResult.ParentId)" TreeColumnIndex="0" ExpandStateMapping="false"

HasChildMapping="@nameof(TraceabilityResult.IsParent)" LoadChildOnDemand="true" Query="@_trQuery">

<SfDataManager Url="TraceabilityResults" Adaptor="Adaptors.ODataV4Adaptor" CrossDomain="true" HttpClientInstance="ApisService.CoreHttpClientOData" />

code

{

private Query? _trQuery { get; set; } = null;

_trQuery = new Query().Where("TrRequestID", "equal", _requestId);

}

Traces : 

fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]

      Unhandled exception in circuit 'pC9YMAsF1Kaz25jfRfpd8b4019Yh87BPIWuO7RNuAns'.

      System.NullReferenceException: Object reference not set to an instance of an object.

         at Syncfusion.Blazor.TreeGrid.Internal.Edit`1.ActionBegin(ActionEventArgs`1 args, ActionEventArgs`1 remoteArgs) at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)

         at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)

         at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<.cctor>b__23_0(Object state)

         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

      --- End of stack trace from previous location ---

         at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

         at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

         at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteBackground(WorkItem item)

warn: Microsoft.AspNetCore.Components.Web.ErrorBoundary[100]

      Unhandled exception rendering component: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

      System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

         at Syncfusion.Blazor.TreeGrid.Internal.GridRenderer`1.CreateEventHandler(Object args)

         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

Thanks you


7 Replies

SM Shek Mohammed Asiq Abdul Jabbar Syncfusion Team October 8, 2024 03:12 PM UTC

Hi Julian,

Greetings from Syncfusion support.


Please find the responses to your queries below:


Query #1: The TreeGrid displays all data initially and after 1 second it refreshes with new data. Subsequently, the razor page is blocked, and the traces provide the following information (see below).


Based on the call stack you provided, we suspect that the issue was caused due to no columns found in treegrid. We must define the columns using TreeGridColumn. We need additional information to check this issue further.

  1. Have you defined the columns in treegrid as we defined in the sample given below?
  2. Also are you updating the Query in any of the actions?
  3. Kindly bound the OnActionFailure event, and share the exceptions if any.


Query #2: Data appears expanded despite using the property `ExpandStateMapping="false"`

The behavior of `ExpandStateMapping` property is different. It ensures that each parent record in datasource retain their expanded or collapsed state at initial rendering or refresh. This property corresponds to a value within the data object of the data source, signifying the expand/collapse status of the parent row.


Code snippet:

<SfTreeGrid TValue="TaskData" AllowSorting="true" AllowFiltering="true" AllowPaging="true" AllowResizing="true"

            AllowSelection="false" EnableHover="false" AllowExcelExport="true" AllowPdfExport="true" Height="100%" Width="100%" IdMapping=" ID" ParentIdMapping="ParentId" ExpandStateMapping="isExpandstate" HasChildMapping="isParent" Query="@GridQuery"

         Toolbar="@(new List<string>(){ "Add", "Edit", "Update", "Cancel", "Delete"})">

public class TaskData

{

    . . . .

    public bool? isExpandstate { get; set; }

    . . . .

}


In the shared sample, the datasource object has an isExpanded property with a Boolean value. Based on this value, the parent rows are rendered in an expanded or collapsed state. Attached the sample for reference.

Also we reviewed the code snippet you shared. It seems that you have enabled the `LoadChildOnDemand` feature, which causes parent records to render in an expanded state by default.


Note:
 We introduced a breaking change in the `LoadChildOnDemand` feature in the Volume 3 2024 main release (v27.1.48). With this update, parent records are initially rendered in a collapsed state, and child records are only loaded when the parent row is expanded when we set the LoadChildOnDemand to true.


Release Notes:
https://blazor.syncfusion.com/documentation/release-notes/27.1.48?type=all#tree-grid


Please let us know if you need further assistance or clarification.


Regards,

Shek


Attachment: WepApiAdap_d40a5b25.zip


JG Julien Gambini October 11, 2024 06:03 PM UTC

Hi Shek, thanks for your reply

Query #1: As you can see below i think i defined good the columns in treegrid because it's works pretty well with datasource  property. I updated syncfusion in v 27.1.48 and the issue is still appears.

I tried the WepApiAdap.zip and i have also an issue (join both pictures)

I noticed that property onDataBound not working.

<TreeGridEvents TValue="TraceabilityResult" DataBound="InitToolbar" OnActionFailure="GridActionFailureHandler" /> and issue is not detected by OnActionFailure.

Query #2: I was focus on the first one for the moment.

I saw the new property TreeGrid.GetTotalItemsCountAsync() to have the number total, i don't know if it's possible to have the information after query execution.

Thanks


Attachment: Errors_7aaf90f5.zip


SM Shek Mohammed Asiq Abdul Jabbar Syncfusion Team October 14, 2024 02:31 PM UTC

Hi Julian,


Query #1: As you can see below, I think I defined good the columns in treegrid because it's works pretty well with datasource property. I updated syncfusion in v 27.1.48 and the issue still appears.


Sorry for the inconvenience caused.


We can replicate the issue from the shared sample. That is because of the Get method not handled properly for the query from our end. We have created a new sample which handles the query property.


Sample attached with this response.


Quert : I noticed that property onDataBound not working.


We suspect that the DataBound event not triggered on initial rendering. We cannot replicate the issue from our the above sample. The DataBound event triggered properly on the initial load. Kindly share the complete treegrid code snippet to replicate the DataBound not triggered issue and project exception from our end.


Query #2: I saw the new property TreeGrid.GetTotalItemsCountAsync() to have the number total, i don't know if it's possible to have the information after query execution.


We can get the number of TotalItemsCount of the dataSource in DataBound event once the data bound in treegrid.


Code-snippet:

public void DataBound()

{

    var totalRecords = tree.GetTotalItemsCountAsync();

}


Note: The abode methos will display the number of records at the view port.


Kindly get back to us for further assistance.


Regards,

Shek


Attachment: SyncfusionBlazorAppEF_4f5c7f38.zip


JG Julien Gambini October 16, 2024 01:53 PM UTC

Hi Shek,

I changed the query because i always have every row of Tasks table and this is not the subject of my request. I want to filter on data with Query and datamanager property.

i join you the same example with issue :

For example, I want to display rows in TreeGrid for Duration=12. But I can't modify the Tasks table data (TaskID : 79878 must have Duration=12 too) to display Task ID 1 / 2 / 5 and 10 when it's collapse, ok ?

You can launch app and then go to datagrid feature page and go back to TreeGrid page and see the issue.

Thanks


Attachment: SyncfusionBlazorAppEF_Issue_ed02458.zip


SM Shek Mohammed Asiq Abdul Jabbar Syncfusion Team October 17, 2024 02:28 PM UTC

Hi Julian,


To filter the duration on initial rendering, we need to customize the dataSource based on the filter value in server. We have customized the server and modified the sample. Please refer to the following code snippets.


public object Get()

{

    var queryString = Request.Query;

    IQueryable<TreeGridWebApiEFSample.Shared.Models.Task> data1 = db.GetAllRecords().AsQueryable();

    . . . . .

 

    if (queryString.Keys.Contains("$filter") && queryString.Keys.Count > 2)

    {

        StringValues filter;

        var filteredRecords = data1;

        queryString.TryGetValue("$filter", out filter);

 

        if (!filter.ToList()[0].Contains("null"))

        {

            // Extract the filter property and value

            var fltr = filter[0].ToString().Split("eq")[1].Trim();  // Filter value (e.g., "12" or "13")

            var prop = filter[0].ToString().Split("eq")[0].Trim();  // Property name (e.g., "Duration")

 

            // Check if the property exists in the model

            if (data1.ToList()[0].GetType().GetProperty(prop)?.Name != null)

            {

                // Apply filtering on both parent and child records

                filteredRecords = data1.Where(data =>

                    data.GetType().GetProperty(prop).GetValue(data, null) != null &&

                    data.GetType().GetProperty(prop).GetValue(data, null).ToString().Contains(fltr)).AsQueryable();

            }

 

            // Find the IDs of the parents whose children matched the filter

            var filteredParentIds = filteredRecords.Where(x => x.ParentID != null)

                                                   .Select(x => x.ParentID)

                                                   .Distinct()

                                                   .ToList();  // ParentIDs of children that match the filter

 

            // Include parent records if they matched the filter, and also those whose children matched the filter

            var parentsWithMatchingChildren = data1.Where(data => filteredParentIds.Contains(data.TaskID)).ToList();

 

            // Filter to show only the children that match or all children of matching parents

            var childRecords = filteredRecords.Where(x => x.ParentID != null).ToList();

            var allFilteredRecords = parentsWithMatchingChildren

                                     .Union(filteredRecords.Where(x => x.ParentID == null))  // Include matching parents

                                     .Union(childRecords)  // Include only matching children or all children for matched parents

                                     .Distinct()

                                     .ToList();

 

            // Now filter to keep only the relevant children (those whose parents matched or they themselves matched)

            var parentIdsWithMatchingChildren = allFilteredRecords.Where(p => p.ParentID == null)

                                                                  .Select(p => p.TaskID)

                                                                  .ToList();

 

            // Update child records: Keep only children whose parent matches the filter or themselves match the filter

            var matchingChildRecords = allFilteredRecords.Where(x => parentIdsWithMatchingChildren.Contains(x.ParentID ?? 0) || x.ParentID == null).ToList();

 

            // COLLAPSING LOGIC: Show only the parent records (ParentID == null)

            var parentRecords = matchingChildRecords.Where(data => data.ParentID == null).AsQueryable();

 

            // Optionally, maintain the hierarchy for expansion

            var hierarchy = parentRecords.Select(parent => new

            {

                Parent = parent,

                Children = matchingChildRecords.Where(child => child.ParentID == parent.TaskID).ToList()  // Find the children of each parent

            }).ToList();

 

            return new

            {

                Items = parentRecords,  // Or return hierarchy if you want to include the full hierarchy structure

                Count = parentRecords.Count()  // Count of top-level records (collapsed view)

            };

        }

    }

    . . . . .

}


We have encountered an issue when expanding a record in the tree grid. It appears that the entire child set binds to the expanding parent due to the filter value being set to the expanding record. Currently, the filter value that we pass during the initial rendering using the query is not retained when expanding a row. This causes unintended behavior where the filtered child records are not correctly displayed.


We have considered the above issue (“Unable to get query value on expanding”) as a bug. Thank you for taking the time to report this issue and helping us improve our product. At Syncfusion, we are committed to fixing all validated defects (subject to technological feasibility and Product Development Life Cycle) and will include the fix in our upcoming NuGet release which is expected to be rolled out on November 05, 2024. Until then we appreciate your patience.


You can now track the status of your request, review the proposed resolution timeline, and contact us for any further inquiries through this link. 

https://www.syncfusion.com/feedback/62347/unable-to-get-query-value-on-expanding


Note: To view the above feedback, kindly login into your account. 


Disclaimer: Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.


Regards,

Shek


Attachment: SyncfusionBlazorAppEF_217110cc.zip


JG Julien Gambini November 6, 2024 11:07 AM UTC

Hi Shek,


i updated the last syncfusion version for issue "Unable to get query value on expanding"

It's better but not perfect (on example and project)


So, i send you the example with modifications for DataBound and Filtered issue

I joined on the zip a word file Issues.docs with screenshoots and code project

Thanks


Attachment: SyncfusionBlazorAppEF_Problems_35475227.zip


SM Shek Mohammed Asiq Abdul Jabbar Syncfusion Team November 6, 2024 01:28 PM UTC

Hi Julian,


With the latest patch release, we recommend including the expanding key in the query along with the initial query to fetch the child records based on the parent ID of the expanded node. To implement this, please refer to the following code snippet:


 

        [HttpGet]

        public object Get()

        {

            var queryString = Request.Query;

            IQueryable<TreeGridWebApiEFSample.Shared.Models.Task> data1 = db.GetAllRecords().AsQueryable();

            IQueryable<TreeGridWebApiEFSample.Shared.Models.Task> data2;
            . . . . . . .

if (queryString.Keys.Contains("$filter") && !queryString.Keys.Contains("$top"))

{

    if (queryString.TryGetValue("$filter", out StringValues filter) && filter.Count > 0)

    {

        var filterParts = filter[0].Split("and");

 

        if (filterParts.Length >= 2)

        {

            // Extract and parse the filter values with trimming and safe parsing

            var fltrString = filterParts[0].Split("eq")[1].Trim();

            var fltr2String = filterParts[1].Split("eq")[1].Trim();

 

            if (int.TryParse(fltrString, out int fltr) && int.TryParse(fltr2String, out int fltr2))

            {

                // Apply the filters in the LINQ query

                data2 = data1.Where(f => f.Duration == fltr && f.ParentID == fltr2).AsQueryable();

 

                return new { Items = data2, Count = data2.Count() };

            }

        }

    }

 

    // Return an empty result if parsing fails

    return new { Items = Enumerable.Empty<object>().AsQueryable(), Count = 0 };

        }



We have attached sample for your reference. If you have any doubts, kindly get back to us.


Regards,

Shek


Attachment: SyncfusionBlazorAppEF_2424dbeb.zip

Loader.
Up arrow icon