Maximize Performance with Load-on-Demand and Virtualization Features in Essential JS 2 TreeGrid

Jollen Moyani - Apr 26 '23 - - Dev Community

The Syncfusion Essential JS 2 TreeGrid component is a versatile and powerful tool for displaying hierarchical data in a structured, easy-to-navigate format. In addition to being available in JavaScript, the TreeGrid component can be used in the ASP.NET (Core, MVC), React, Angular, and Vue frameworks.

Tree grids are a great way to represent hierarchical data in a tabular format, but they might become slow and unwieldy when dealing with large data sets. This is where the load-on-demand and virtualization features come in handy.

The load-on-demand feature allows you to load records from remote services only when they are requested by the user, instead of loading all records at once. Virtualization renders the row elements for the current viewport only and other row elements while scrolling in the TreeGrid.

These features are beneficial when dealing with large data sets that can cause performance issues if they are all loaded at once.

Let’s see how the load-on-demand and virtualization features enhance performance and the user experience while handling a huge volume of data in the Essential JS 2 TreeGrid.

How to enable load-on-demand and virtualization features in the TreeGrid

Note: This guide is based on the Syncfusion ASP.NET Core Tree Grid component. If you are new to this platform, please visit the getting started page before proceeding.

Component-side configurations

We’ll use the data manager to provide the data source from a remote URL.

Refer to the following code example.

<ejs-treegrid id="TreeGrid" 
              idMapping="TaskID" 
              parentIdMapping="ParentValue" 
              loadChildOnDemand="true" 
              hasChildMapping="isParent" 
              treeColumnIndex="1" 
              enableVirtualization="true" 
              height="400">
 <e-data-manager url="/Home/DataSource" 
                 adaptor="UrlAdaptor" 
                 e-data-manager></ejs-treegrid>
Enter fullscreen mode Exit fullscreen mode

In this code example:

  • idMapping and parentIdMapping—These properties connect self-referential data from remote services in parent-child relationships.
  • HasChildMapping—This property represents the data objects in the data source, indicating whether the current record is a parent record. The TreeGrid can’t identify the records that have child records when data is loaded on-demand.
  • LoadChildOnDemand—This property is optional. It loads all the parent records in an expanded state with their child records when enabled. In this blog, we will enable this property.
  • EnableVirtualization—Enable this property to load only the records needed for the current viewport. The remaining data will be loaded on demand while scrolling. Without this property, the TreeGrid will load all the parent records simultaneously, thus affecting the performance.

These configurations are needed on the component side to enable load-on-demand and virtualization features in the TreeGrid.

Note: The paging feature loads all the child records at once. Thus, it affects performance. But the virtual scrolling feature loads only the child records necessary for the current viewport and dynamically loads other records during vertical scrolling.

Server-side remote service configurations

For every action, like scrolling to the next record set or expanding the parent records, the TreeGrid will request the server return the next data set. So, these requests must be handled in the remote service, and it will return the required data based on the request.

The requests from the TreeGrid component are sent to the DataManagerRequest as parameters. The data operations will be performed and returned to the client TreeGrid component based on the parameters.

Refer to the following code example with inline comments.

public IActionResult DataSource([FromBody] DataManagerRequest dm)
{
   List<TreeData> data = new List<TreeData>();
   data = TreeData.GetTree();
   DataOperations operation = new DataOperations();
   IEnumerable<TreeData> DataSource = data;

   if (!(dm.Where != null && dm.Where.Count > 1))
   {
      data = data.Where(p => p.ParentValue == null).ToList(); //filter root parent records for further easy data operations

   }
   DataSource = data;
   if (dm.Where != null && dm.Where.Count > 1)
   {
      DataSource = operation.PerformFiltering(DataSource, dm.Where, "and");
   }
   data = new List<TreeData>();
   foreach (var rec in DataSource)
   {
      data.Add(rec as TreeData);
   }

   // Filter parent and child records for the current viewport
   var GroupData = TreeData.GetTree().ToList().GroupBy(rec => rec.ParentValue).Where(g => g.Key != null).ToDictionary(g => g.Key?.ToString(), g => g.ToList());
   foreach (var Record in data.ToList())
   {
       if (GroupData.ContainsKey(Record.TaskID.ToString()))
       {
          var ChildGroup = GroupData[Record.TaskID.ToString()];
          if (ChildGroup?.Count > 0)
             AppendChildren(dm, ChildGroup, Record, GroupData, data);
       }
    }
    DataSource = data;

    if (dm.Expand != null && dm.Expand[0] == "CollapsingAction") // setting the skip index based on collapsed parent
     {
        string IdMapping = "TaskID";
        List<WhereFilter> CollapseFilter = new List<WhereFilter>();
        CollapseFilter.Add(new WhereFilter() { Field = IdMapping, value = dm.Where[0].value, Operator = dm.Where[0].Operator });
        var CollapsedParentRecord = operation.PerformFiltering(DataSource, CollapseFilter, "and");
        var index = data.Cast<object>().ToList().IndexOf(CollapsedParentRecord.Cast<object>().ToList()[0]);
        dm.Skip = index;
     }
     else if (dm.Expand != null && dm.Expand[0] == "ExpandingAction") // setting the skip index based on expanded parent
     {
        string IdMapping = "TaskID";
        List<WhereFilter> ExpandFilter = new List<WhereFilter>();
        ExpandFilter.Add(new WhereFilter() { Field = IdMapping, value = dm.Where[0].value, Operator = dm.Where[0].Operator });
        var ExpandedParentRecord = operation.PerformFiltering(DataSource, ExpandFilter, "and");
        var index = data.Cast<object>().ToList().IndexOf(ExpandedParentRecord.Cast<object>().ToList()[0]);
        dm.Skip = index;
     }
     int count = data.Count;
     DataSource = data;

     //Paging operation
     if (dm.Skip != 0)
     {
        DataSource = operation.PerformSkip(DataSource, dm.Skip);  
     }
     if (dm.Take != 0)
     {
        DataSource = operation.PerformTake(DataSource, dm.Take);
     }
     return dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource);

}
Enter fullscreen mode Exit fullscreen mode

Performance metrics

Let’s compare the performance metrics for rendering 10,000 records with and without load-on-demand and virtualization features in the TreeGrid.

Without virtualization (normal scrolling) and loading data at once using data binding

With virtualization and load-on-demand data binding

~12 seconds

~800 milliseconds

From this table data, it is evident that with load-on-demand and virtualization, the performance of the TreeGrid can be drastically enhanced.

GitHub reference

Check out the complete code example for Load-on-demand and virtualization features in the Essential JS 2 TreeGrid on GitHub.

Conclusion

Thanks for reading! In this blog, we’ve seen how the load-on-demand and virtualization features help us to enhance performance and the user experience in the Essential JS 2 TreeGrid. Please visit the TreeGrid’s online demos and documentation for more information. We appreciate your feedback, which you can leave in the comments section below.

You can download our free trial if you do not have a Syncfusion license but wish to try the TreeGrid component.

For questions, you can contact us via our support forums, support portal, or feedback portal. We are always happy to assist you!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .