Load On Demand Group Data
The grid component lets you load the data for each individual group on demand, instead of having it always present.
In this article:
Basics
To enable load-on-demand for the groups, set the LoadGroupsOnDemand
parameter of the grid to true
. In this mode, the grid behaves as usual while there is no grouping, and you can use this together with Virtual Scrolling for the rows.
Once grouping is applied (either manually by the user, or through the grid state), the groups will now show up collapsed by default. When a group is expanded by the user its data is requested from the data source - if you provide all the Data
to the grid, the grid will perform the operations for you, for details about server operations, see below.
Each group header, each group footer and the grid footer will count as rows in the grid for the purposes of paging. Until you expand a group, its child items are not counted and shown in the Total
count for the purposes of paging.
Server Operations
When loading data on demand through the OnRead event, there can be three different kinds of requests, depending on the data that is needed:
If there is no grouping, the request is as usual - no additional parameters or settings are added to it by the grid.
-
If there is grouping and the grid needs a list of groups, the
GroupPaging
parameter of itsDataSourceRequest
will be set totrue
.If the currently expanded group row has subgroups, a request is sent with the
GroupPaging
parameter set totrue
, prompting that the response must include the total of items in the sub group and return a collection of groups once again, instead of a collection of models.If the grid starts with grouping set, it will make one request for the list of all the groups, and will keep them in memory for paging, so a paging operation will not call
OnRead
again.
If the currently expanded group row does not have subgroups, the
Filter
parameter of theDataSourceRequest
will contain the group value (and the values of any subgroups) for which the items are requested. ThePageSize
of that request is set to0
so the grid gets all items for that group. TheOnRead
event will fire every time you expand a group to get the items for that group.
Examples
This section contains the following examples:
- Regular Paging and Group Load On Demand - a basic example how to enable the feature
- Virtual Scrolling, Group Load On Demand and Server Data Operations - mimics an actual data service to implement load on demand for the data when the user expands a group or when they scroll to need a new set of available groups. Also showcases how to set the initial grid state to include grouping.
Regular Paging and Group Load On Demand
This example shows the basics of enabling the group load on demand - setting LoadGroupsOnDemand="true"
. Group the grid by the Team and/or Vacation columns to see the effect.
Drag the column header of the "Team" and/or "On Vacation" column to the group panel at the top
<TelerikGrid Data=@GridData
LoadGroupsOnDemand="true"
Groupable="true"
Navigable="true" Pageable="true" Sortable="true" FilterMode="@GridFilterMode.FilterRow">
<GridAggregates>
<GridAggregate Field="@nameof(Employee.Team)" Aggregate="@GridAggregateType.Count" />
<GridAggregate Field="@nameof(Employee.Salary)" Aggregate="@GridAggregateType.Min" />
<GridAggregate Field="@nameof(Employee.Salary)" Aggregate="@GridAggregateType.Sum" />
<GridAggregate Field="@nameof(Employee.IsOnLeave)" Aggregate="@GridAggregateType.Count" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(Employee.Name)" Groupable="false" />
<GridColumn Field="@nameof(Employee.Team)" Title="Team">
<GroupHeaderTemplate>
Employees in this group: @context.Count
</GroupHeaderTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.Salary)" Groupable="false">
<GroupFooterTemplate>
Lowest salary in this group: @context.Min
</GroupFooterTemplate>
<FooterTemplate>
Total salary expenses @context.Sum
</FooterTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.IsOnLeave)" Title="On Vacation">
<GroupHeaderTemplate>
Employees with "OnLeave" @context.Value : @context.Count
</GroupHeaderTemplate>
</GridColumn>
</GridColumns>
</TelerikGrid>
@code {
public List<Employee> GridData { get; set; }
protected override void OnInitialized()
{
GridData = new List<Employee>();
var rand = new Random();
for (int i = 0; i < 25; i++)
{
GridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3,
IsOnLeave = i % 2 == 0,
Salary = rand.Next(1000, 5000)
});
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public bool IsOnLeave { get; set; }
public decimal Salary { get; set; }
}
}
Virtual Scrolling, Group Load On Demand and Server-side Data Operations
This example shows how you can combine the virtual row scrolling feature with loading group data on demand through a remote service (mocked by a static class in this example so you can run it easily), and how to set the initial state of the grid to have grouping by default.
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
Scroll through the groups or expand them to load their data on demand
<TelerikGrid Data=@GridData
LoadGroupsOnDemand="true"
Groupable="true"
OnStateInit="@((GridStateEventArgs<object> args) => OnStateInitHandler(args))"
OnRead="@ReadItems"
TotalCount="@Total"
ScrollMode="@GridScrollMode.Virtual" PageSize="20" RowHeight="60"
Navigable="true" Sortable="true" FilterMode="@GridFilterMode.FilterRow" Height="600px">
<GridColumns>
<GridColumn Field="@nameof(Employee.Name)" FieldType="@typeof(string)" Groupable="false" />
<GridColumn Field="@nameof(Employee.Team)" FieldType="@typeof(string)" Title="Team" />
<GridColumn Field="@nameof(Employee.Salary)" FieldType="@typeof(decimal)" Groupable="false" />
<GridColumn Field="@nameof(Employee.IsOnLeave)" FieldType="@typeof(bool)" Title="On Vacation" />
</GridColumns>
</TelerikGrid>
@code {
List<object> GridData { get; set; }
int Total { get; set; } = 0;
protected async Task ReadItems(GridReadEventArgs args)
{
// sample data retrieval, see comments in the service mimic class below
DataEnvelope<Employee> result = await MyService.GetData(args.Request);
if (args.Request.Groups.Count > 0)
{
GridData = result.GroupedData.Cast<object>().ToList();
}
else
{
GridData = result.CurrentPageData.Cast<object>().ToList();
}
Total = result.TotalItemCount;
StateHasChanged();
}
void OnStateInitHandler(GridStateEventArgs<object> args)
{
// set initial grouping
GridState<object> desiredState = new GridState<object>()
{
GroupDescriptors = new List<GroupDescriptor>()
{
new GroupDescriptor()
{
Member = "Team",
MemberType = typeof(string)
},
new GroupDescriptor()
{
Member = "IsOnLeave",
MemberType = typeof(bool)
}
}
};
args.GridState = desiredState;
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public bool IsOnLeave { get; set; }
public decimal Salary { get; set; }
}
public class DataEnvelope<T>
{
public List<AggregateFunctionsGroup> GroupedData { get; set; }
public List<T> CurrentPageData { get; set; }
public int TotalItemCount { get; set; }
}
public static class MyService
{
private static List<Employee> SourceData { get; set; }
public static async Task<DataEnvelope<Employee>> GetData(DataSourceRequest request)
{
if (SourceData == null)
{
SourceData = new List<Employee>();
var rand = new Random();
for (int i = 0; i < 2500; i++)
{
SourceData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 100,
IsOnLeave = i % 3 == 0,
Salary = rand.Next(1000, 5000)
});
}
}
await Task.Delay(500);// deliberate delay to showcase async operations, remove in a real app
// retrieve data as needed, you can find more examples and runnable projects here
// https://github.com/telerik/blazor-ui/tree/master/grid/datasourcerequest-on-server
var datasourceResult = SourceData.ToDataSourceResult(request);
DataEnvelope<Employee> dataToReturn;
if (request.Groups.Count > 0)
{
dataToReturn = new DataEnvelope<Employee>
{
GroupedData = datasourceResult.Data.Cast<AggregateFunctionsGroup>().ToList(),
TotalItemCount = datasourceResult.Total
};
}
else
{
dataToReturn = new DataEnvelope<Employee>
{
CurrentPageData = datasourceResult.Data.Cast<Employee>().ToList(),
TotalItemCount = datasourceResult.Total
};
}
return await Task.FromResult(dataToReturn);
}
}
}
Limitations
The expanded state of the groups is preserved during paging only, but not if sorting or filtering is applied.
Since group headers and footers are treated like rows in the grid, the group headers may remain on a previous page from the data when you page the grid.
-
If the group load on demand is used in combination with virtual scrolling:
All requirements and limitations of the virtual scrolling functionality apply.
Aggregates are not supported.
When Exporting only the current page of data from the grid (
AllPages=false
), the exported file will not contain child data for collapsed groups.