Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions components/grid/aggregates.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ position: 23

# Grid Aggregates

The Grid component provides built-in aggregates for column values based on [grouping]({%slug components/grid/features/grouping%}).
The Grid component provides built-in aggregates for column values based on [grouping]({%slug components/grid/features/grouping%}) and also a grand total row.

There are several available aggregate functions under the `Telerik.Blazor.GridAggregateType` enum:

Expand All @@ -24,15 +24,17 @@ The `Count` aggregate can be applied to any type of field. The other aggregates

You can use aggregates in the following templates:

* `GroupFooterTemplate` of a `GridColumn` - a footer in the respective column that renders when the grid is grouped.
* `GroupHeaderTemplate` of a `GridColumn` - a header in the respective column that renders when the grid is grouped by that column. The `Value` field in the context carries the current group value.
* [`GroupFooterTemplate`]({%slug grid-templates-column-group-footer%}) of a `GridColumn` - a footer in the respective column that renders when the grid is grouped.
* [`GroupHeaderTemplate`]({%slug grid-templates-group-header%}) of a `GridColumn` - a header in the respective column that renders when the grid is grouped by that column. The `Value` field in the context carries the current group value.
* [`FooterTemplate`]({%slug grid-templates-column-footer%}) of a `GridColumn` - a grand total row of footers for the entire grid.

To enable aggregates:

1. Set the grid's `Groupable` property to `true`.
1. Under the `GridAggregates` tag, define the `GridAggregate` entries to enable the aggregations per field you want to use.
1. Use the aggregate result in the templates that support it - their `context` is strongly typed and carries the aggregate values in the respective fields.
1. Group the grid to see the effect
1. Set the grid's `Groupable` property to `true`.
* If you will be using only `FooterTemplate`s - grouping is not required.
1. Group the grid to see the effect on group-specific templates

You should define only aggregates that you will use to avoid unnecessary calculations that may be noticeable on large data sets.

Expand All @@ -42,16 +44,30 @@ If you try to use an aggregate that is not defined, or an aggregate over an unsu
>caption Use Aggregates in the Telerik Blazor Grid

````CSHTML
@* Enable and use aggregates. To see the effect, group by a column - "Team" and then "Active Projects" *@
@* Enable and use aggregates. To see the full effect, group by a column - "Team" and then "Active Projects" *@

<TelerikGrid Data=@GridData Groupable="true" Pageable="true" Height="650px">
<TelerikGrid Data=@GridData Groupable="true" Pageable="true" Height="700px">
<GridAggregates>
<GridAggregate Field=@nameof(Employee.Name) Aggregate="@GridAggregateType.Count" />
<GridAggregate Field=@nameof(Employee.Team) Aggregate="@GridAggregateType.Count" />
<GridAggregate Field=@nameof(Employee.Salary) Aggregate="@GridAggregateType.Max" />
<GridAggregate Field=@nameof(Employee.Salary) Aggregate="@GridAggregateType.Sum" />
</GridAggregates>
<GridColumns>
<GridColumn Field=@nameof(Employee.Name) Groupable="false" />
<GridColumn Field=@nameof(Employee.Name) Groupable="false">
<FooterTemplate>
Total: @context.Count employees.
<br />
@{
// you can use aggregates for other fields/columns by extracting the desired one by its
// field name and aggregate function from the AggregateResults collection
// The type of its Value is determined by the type of its field - decimal for the Salary field here
decimal salaries = (decimal)context.AggregateResults
.FirstOrDefault(r => r.AggregateMethodName == "Sum" && r.Member == "Salary")?.Value;
}
Total salaries: @salaries.ToString("C0")
</FooterTemplate>
</GridColumn>
<GridColumn Field=@nameof(Employee.Team) Title="Team">
<GroupFooterTemplate>
Team Members: <strong>@context.Count</strong>
Expand All @@ -64,9 +80,9 @@ If you try to use an aggregate that is not defined, or an aggregate over an unsu
<GridColumn Field=@nameof(Employee.Salary) Title="Salary" Groupable="false">
<GroupFooterTemplate>
@* you can use a group footer for non-groupable columns as well *@
Total montly salary: @context.Sum
Total salaries: @context.Sum
<br />
<span style="color: red;">Top paid employee: @context.Max</span>
<span style="color: red;">Highest: @context.Max</span>
</GroupFooterTemplate>
</GridColumn>
<GridColumn Field=@nameof(Employee.ActiveProjects) Title="Active Projects">
Expand All @@ -75,7 +91,7 @@ If you try to use an aggregate that is not defined, or an aggregate over an unsu
<span>Currently active projects: @context.Value &nbsp;</span>

//sample of conditional logic in the group header
if ( (int)context.Value > 3) // in a real case, you may want to ensure type safety and add defensive checks
if ((int)context.Value > 3) // in a real case, you may want to ensure type safety and add defensive checks
{
<strong style="color: red;">These people work on too many projects</strong>
}
Expand Down
Binary file modified components/grid/images/grid-aggregates-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
222 changes: 222 additions & 0 deletions components/grid/templates/column-footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
---
title: Column Footer
page_title: Grid - Column Footer Template
description: Use custom column footer templates for grand total data in Grid for Blazor.
slug: grid-templates-column-footer
tags: telerik,blazor,grid,templates,column,footer,grand,total
published: True
position: 20
---

# Column Footer Template

You can display a grand total row at the bottom of the grid through the `FooterTemplate` of each [bound]({%slug components/grid/columns/bound%}) column.

You can use [aggregates]({%slug grid-aggregates%}) for the current field directly from the `context`, and its `AggregateResults` field lets you get aggregates for other fields that you have defined through their field name and aggregate function.


>caption Footer Template with grand total data

````CSHTML
@* grand total footer that is always visible *@

<TelerikGrid Data=@GridData Pageable="true" Height="300px">
<GridAggregates>
<GridAggregate Field=@nameof(Employee.Salary) Aggregate="@GridAggregateType.Max" />
<GridAggregate Field=@nameof(Employee.Salary) Aggregate="@GridAggregateType.Sum" />
<GridAggregate Field=@nameof(Employee.EmployeeId) Aggregate="@GridAggregateType.Count" />
</GridAggregates>
<GridColumns>
<GridColumn Field=@nameof(Employee.Salary) Title="Salary">
<FooterTemplate>
Total salaries: @context.Sum.Value.ToString("C0")
<br />
Highest salary: @context.Max.Value.ToString("C0")
</FooterTemplate>
</GridColumn>
<GridColumn Field=@nameof(Employee.Name)>
<FooterTemplate>
@{
// you can use aggregates for other fields/columns by extracting the desired one by its
// field name and aggregate function from the AggregateResults collection
// The type of its Value is determined by the type of its field - decimal for the Salary field here
int headCount = (int)context.AggregateResults
.FirstOrDefault(r => r.AggregateMethodName == "Count" && r.Member == nameof(Employee.EmployeeId))?.Value;
}
Total employees: @headCount
</FooterTemplate>
</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 < 15; i++)
{
Random rnd = new Random();
GridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Salary = rnd.Next(1000, 5000),
});
}
}

public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
}
````

>caption The result from the code snippet above

![](images/footer-template.png)


## Notes

Since the purpose of the footer template is to display aggregates, you need to be aware of their behavior. The following list expands on that and other things to keep in mind.

* Aggregate results are based on all the data across all pages.

* Aggregate results are calculated over filtered data only.

* When using the [`OnRead event`]({%slug components/grid/manual-operations%}), the aggregates are based on the available data - for the current page only. If you want to aggregate over the entire data source, you should get the desired information into the view model with the appropriate code in the application, instead of using the built-in grid Aggregates. The `Aggregate` extension method from our `Telerik.DataSource.Extensions` namespace can help you calculate them.

**Razor**

@using Telerik.DataSource.Extensions
@using Telerik.DataSource

The current data aggregates will differ from the aggregates on all the data

<TelerikGrid Data=@GridData TotalCount=@Total OnRead=@ReadItems Pageable="true">
<GridColumns>
<GridColumn Field=@nameof(Employee.ID)>
<FooterTemplate>
Total employees: @totalEmployees
<hr />
Total employees (from current data): @context.Count
</FooterTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.Salary)">
<FooterTemplate>
Top salary: @highestSalary
<hr />
Top salary (from current data): @context.Max
</FooterTemplate>
</GridColumn>
<GridColumn Field=@nameof(Employee.Name) Title="Name">
</GridColumn>
</GridColumns>
<GridAggregates>
<GridAggregate Field=@nameof(Employee.Salary) Aggregate="@GridAggregateType.Max" />
<GridAggregate Field=@nameof(Employee.ID) Aggregate="@GridAggregateType.Count" />
</GridAggregates>
</TelerikGrid>

@code {
public List<Employee> SourceData { get; set; }
public List<Employee> GridData { get; set; }
public int Total { get; set; } = 0;

// values for the "real" aggregations
int totalEmployees { get; set; }
decimal highestSalary { get; set; }

protected override void OnInitialized()
{
SourceData = GenerateData();
}

protected async Task ReadItems(GridReadEventArgs args)
{
var datasourceResult = SourceData.ToDataSourceResult(args.Request);

GridData = (datasourceResult.Data as IEnumerable<Employee>).ToList();
Total = datasourceResult.Total;

// use Telerik Extension methods to aggregate the entire data source per the aggregates defined in the grid
// in a real case, this code should be in a controller that can query the database directly. We cast here for simplicity
IQueryable allDataAsQueriable = SourceData.AsQueryable();

// get the aggregate functions from the grid data source request
IEnumerable<AggregateFunction> gridAggregates = Enumerable.Empty<AggregateFunction>();
if (args.Request.Aggregates.Count == 0)
{
// aggregate descriptors - the ones from the markup will not be present in the first call to OnRead
// because the framework initializes child components too late and the GridAggregates component is not available yet
gridAggregates = new List<AggregateFunction>()
{
new MaxFunction()
{
SourceField = nameof(Employee.Salary)
},
new CountFunction()
{
SourceField = nameof(Employee.ID)
}
};
}
else
{
gridAggregates = args.Request.Aggregates.SelectMany(a => a.Aggregates);
}

// use the Telerik Aggregate() extension method to perform aggregation on the IQueryable collection
AggregateResultCollection aggregatedResults = allDataAsQueriable.Aggregate(gridAggregates);

// extract the aggregate data like you would within the footer template - by the function and field name
// and put it in the view-model. In a real case that would be extra data returned in the response
totalEmployees = (int)aggregatedResults.FirstOrDefault(
r => r.AggregateMethodName == "Count" && r.Member == nameof(Employee.ID))?.Value;

highestSalary = (decimal)aggregatedResults.FirstOrDefault(
r => r.AggregateMethodName == "Max" && r.Member == nameof(Employee.Salary))?.Value;


// for the grid data update itself
StateHasChanged();
}

private List<Employee> GenerateData()
{
var result = new List<Employee>();
var rand = new Random();
for (int i = 0; i < 100; i++)
{
result.Add(new Employee()
{
ID = i,
Name = "Name " + i,
Salary = rand.Next(1000, 5000),
});
}

return result;
}

public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
}

* Footer Templates are not available for the `GridCheckboxColumn` and the `GridCommandColumn`.


## See Also

* [Live Demo: Grid Footer Template](https://demos.telerik.com/blazor-ui/grid/footer-template)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.