Get Filtered and Sorted Data from Grid
Environment
Product | Grid for Blazor |
Description
This KB article answers the following questions. All of them revolve around the Grid table rows, which the component is currently displaying to the user.
- How to obtain the filtered data in the Grid? This applies to filtering and the
SearchBox
. - How do I know the filtered item count? How to get how many rows are in the filtered Grid?
- How to find only the used (displayed) items the Grid data list?
- How can I get the Grid data after filtering or sorting?
- How to get the sorted data in the Grid? How to get the Grid items in the order that is on the user screen?
- Is a way to get current Grid state data?
- How to find the next or previous data row if the user has sorted the Grid? In this case the original table row index doesn't match the index in the sorted data.
- How to get the clicked row index if the Grid is sorted or filtered?
Solution
The solution is different when:
Things to consider:
- When using
OnRead
, there will be less custom code and no need to repeat the internal Grid data operations manually. - When the Grid data is grouped, the
DataSourceResult.Data
collection contains members of typeAggregateFunctionsGroup
instead of Grid model class objects.
When Using Data Parameter
- Obtain the Grid state in the Grid
OnStateChanged
event or with the GridGetState()
method. The exact approach depends on if you want to get the current visible Grid data automatically or on demand. - Create a new
DataSourceRequest
object. Populate its properties with the respective information from theGridState
object. Note that theFilters
property of theDataSourceRequest
will have to include filter descriptors from twoGridState
properties -FilterDescriptors
andSearchFilter
. - (optional) If you want to get the currently filtered and sorted data from all Grid pages, do not set the
Page
,PageSize
, andSkip
properties of theDataSourceRequest
object. - Execute the
ToDataSourceResult()
extension method on the GridData
collection. You will need to import theTelerik.DataSource.Extensions
namespace. - The currently visible Grid data will be in the
Data
property of theDataSourceResult
object, which is returned byToDataSourceResult()
. The total Grid item count (on all pages) will be in theTotal
property.
The most important part of the example below is in the RequestCurrentMainGridData()
method.
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikButton OnClick="@GetCurrentMainGridData">Get Main Grid Data</TelerikButton>
or
<label>
<TelerikCheckBox @bind-Value="@ShouldUseOnStateChanged" />
Detect Main Grid Changes Automatically
</label>
<h2>Main Grid</h2>
<TelerikGrid @ref="@GridRef"
Data="@GridData"
TItem="@Product"
Pageable="true"
PageSize="@GridPageSize"
Sortable="true"
FilterMode="GridFilterMode.FilterRow"
Groupable="true"
OnStateChanged="@OnGridStateChanged">
<GridToolBarTemplate>
<GridSearchBox />
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.InProduction)" />
</GridColumns>
</TelerikGrid>
<h2>Current Page in Main Grid</h2>
<p>Showing @( Math.Min(GridPageSize, CurrentMainGridTotal) ) item(s) out of @CurrentMainGridTotal.</p>
<TelerikGrid Data="@CurrentMainGridData">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.InProduction)" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<Product> GridRef { get; set; } = null!;
private List<Product> GridData { get; set; } = new List<Product>();
private List<Product> CurrentMainGridData { get; set; } = new List<Product>();
private int CurrentMainGridTotal { get; set; }
private int GridPageSize { get; set; } = 4;
private bool ShouldUseOnStateChanged { get; set; } = true;
private void OnGridStateChanged(GridStateEventArgs<Product> args)
{
if (ShouldUseOnStateChanged)
{
RequestCurrentMainGridData(args.GridState);
}
}
private void GetCurrentMainGridData()
{
RequestCurrentMainGridData(GridRef.GetState());
}
private void RequestCurrentMainGridData(GridState<Product> gridState)
{
var filterAndSearchDescriptors = new List<IFilterDescriptor>(gridState.FilterDescriptors);
filterAndSearchDescriptors.Add(gridState.SearchFilter);
var request = new DataSourceRequest()
{
Filters = filterAndSearchDescriptors,
Groups = gridState.GroupDescriptors.ToList(),
Page = gridState.Page ?? 1, // for paging
PageSize = GridPageSize,
Skip = gridState.Skip ?? 0, // for virtual scrolling
Sorts = gridState.SortDescriptors.ToList()
};
var result = GridData.ToDataSourceResult(request);
if (gridState.GroupDescriptors.Any())
{
var groups = result.Data.Cast<AggregateFunctionsGroup>();
CurrentMainGridData = new List<Product>();
foreach (var group in groups)
{
CurrentMainGridData.AddRange(group.Items.Cast<Product>().ToList());
}
}
else
{
CurrentMainGridData = result.Data.Cast<Product>().ToList();
}
CurrentMainGridTotal = result.Total;
}
protected override void OnInitialized()
{
var rnd = new Random();
for (int i = 1; i <= 30; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Price = (decimal)rnd.Next(1, 100),
Stock = rnd.Next(0, 50),
ReleaseDate = DateTime.Now.AddDays(-rnd.Next(60, 1000)),
InProduction = i % 4 == 0
});
}
CurrentMainGridData = GridData.Take(GridPageSize).ToList();
CurrentMainGridTotal = GridData.Count;
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime ReleaseDate { get; set; }
public bool InProduction { get; set; }
}
}
When Using OnRead Event
- Implement the Grid
OnRead
event handler as usual. More Grid-specific examples are available in the Grid Manual Operations article. - The visible data items on the current Grid page are in the
args.Data
collection, which is set in the GridOnRead
handler.args.Data
is a property of theGridReadEventArgs
event argument, so it's not accessible outsideOnRead
. That's why you can cache it in a separate variable, together with the total item count (args.Total
).
Optionally, if you want to get the currently filtered and sorted data from all Grid pages:
- Execute
ToDataSourceResult()
over the Grid datasource as usual. This will produce the data items on the current Grid page. - Reset the
Page
,PageSize
, andSkip
properties of theDataSourceRequest
object in theGridReadEventArgs
event argument. - Repeat the
ToDataSourceResult()
call over the Grid datasource. This will produce the data items from all Grid pages.
The most important part of the example below is in the OnGridRead
handler.
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<h2>Main Grid</h2>
<TelerikGrid OnRead="@OnGridRead"
TItem="@Product"
Pageable="true"
PageSize="@GridPageSize"
Sortable="true"
FilterMode="GridFilterMode.FilterRow"
Groupable="true">
<GridToolBarTemplate>
<GridSearchBox />
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.InProduction)" />
</GridColumns>
</TelerikGrid>
<h2>Current Page in Main Grid</h2>
<p>Showing @( Math.Min(GridPageSize, CurrentMainGridTotal) ) item(s) out of @CurrentMainGridTotal.</p>
<TelerikGrid Data="@CurrentMainGridData">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.InProduction)" />
</GridColumns>
</TelerikGrid>
@code {
private List<Product> GridData { get; set; } = new List<Product>();
private List<Product> CurrentMainGridData { get; set; } = new List<Product>();
private int CurrentMainGridTotal { get; set; }
private int GridPageSize { get; set; } = 4;
private async Task OnGridRead(GridReadEventArgs args)
{
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = CurrentMainGridTotal = result.Total;
if (args.Request.Groups.Any())
{
var groups = result.Data.Cast<AggregateFunctionsGroup>();
CurrentMainGridData = new List<Product>();
foreach (var group in groups)
{
CurrentMainGridData.AddRange(group.Items.Cast<Product>().ToList());
}
}
else
{
CurrentMainGridData = result.Data.Cast<Product>().ToList();
}
}
protected override void OnInitialized()
{
var rnd = new Random();
for (int i = 1; i <= 30; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Price = (decimal)rnd.Next(1, 100),
Stock = rnd.Next(0, 50),
ReleaseDate = DateTime.Now.AddDays(-rnd.Next(60, 1000)),
InProduction = i % 4 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime ReleaseDate { get; set; }
public bool InProduction { get; set; }
}
}