Skip to content

Commit

Permalink
Merge pull request #795 from EnchantedCoder/feature/issue-96
Browse files Browse the repository at this point in the history
[HxGrid] Add support for custom pagination display providers
  • Loading branch information
hakenr committed Apr 24, 2024
2 parents 9eafd21 + a9c70dc commit e8216b2
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 3 deletions.
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Context for the "load more" template of the <see cref="HxGrid{TItem}"/>.
/// </summary>
public sealed class GridLoadMoreTemplateContext
public sealed record GridLoadMoreTemplateContext
{
private readonly Func<Task> _loadMoreAsyncFunc;

Expand Down
@@ -0,0 +1,39 @@
namespace Havit.Blazor.Components.Web.Bootstrap;

/// <summary>
/// Context for the <see cref="HxGrid{TItem}.PaginationTemplate" /> of the <see cref="HxGrid{TItem}"/>.
/// </summary>
public sealed record GridPaginationTemplateContext
{
private readonly Func<int, Task> _changeCurrentPageIndexAsyncFunc;

internal GridPaginationTemplateContext(Func<int, Task> changeCurrentPageIndexAsyncFunc)
{
_changeCurrentPageIndexAsyncFunc = changeCurrentPageIndexAsyncFunc;
}

/// <summary>
/// Current grid user state (contains <see cref="GridUserState.PageIndex"/>).
/// </summary>
public GridUserState CurrentUserState { get; init; }

/// <summary>
/// Items per page.
/// </summary>
public int PageSize { get; init; }

/// <summary>
/// Total number of data items.
/// </summary>
public int TotalCount { get; init; }

/// <summary>
/// Settings for the pager (derived from <c>HxGrid.PagerSettings</c> .. <c>HxGrid.Settings.PagerSettings</c> .. <c>HxGrid.Defaults.PagerSettings</c> cascade).
/// </summary>
public PagerSettings PagerSettings { get; init; }

/// <summary>
/// Instructs the grid to change the current page index.
/// </summary>
public Task ChangeCurrentPageIndexAsync(int pageIndex) => _changeCurrentPageIndexAsyncFunc.Invoke(pageIndex);
}
15 changes: 13 additions & 2 deletions Havit.Blazor.Components.Web.Bootstrap/Grids/HxGrid.razor
Expand Up @@ -256,9 +256,20 @@
@if (((ContentNavigationModeEffective == GridContentNavigationMode.Pagination) || (ContentNavigationModeEffective == GridContentNavigationMode.PaginationAndLoadMore)) && (PageSizeEffective > 0) && (_totalCount != null))
{
int totalPages = (_totalCount.Value + PageSizeEffective - 1) / PageSizeEffective;
if (totalPages > 1)
int currentPageIndex = CurrentUserState.PageIndex + ((CurrentUserState.LoadMoreAdditionalItemsCount + PageSizeEffective - 1) / PageSizeEffective);
if (PaginationTemplate != null)
{
var paginationContext = new GridPaginationTemplateContext(HandlePagerCurrentPageIndexChanged)
{
CurrentUserState = CurrentUserState with { },
PageSize = PageSizeEffective,
TotalCount = _totalCount.Value,
PagerSettings = PagerSettingsEffective,
};
@PaginationTemplate(paginationContext)
}
else if (totalPages > 1) // if there is only one page, we do not need to render the pager (default behavior, the PaginationTemplate can override this behavior)
{
int currentPageIndex = CurrentUserState.PageIndex + ((CurrentUserState.LoadMoreAdditionalItemsCount + PageSizeEffective - 1) / PageSizeEffective);
<HxPager TotalPages="@totalPages"
CurrentPageIndex="@currentPageIndex"
CurrentPageIndexChanged="@HandlePagerCurrentPageIndexChanged"
Expand Down
7 changes: 7 additions & 0 deletions Havit.Blazor.Components.Web.Bootstrap/Grids/HxGrid.razor.cs
Expand Up @@ -67,6 +67,11 @@ public partial class HxGrid<TItem> : ComponentBase, IDisposable
/// </summary>
[Parameter] public RenderFragment EmptyDataTemplate { get; set; }

/// <summary>
/// Template for rendering custom pagination.
/// </summary>
[Parameter] public RenderFragment<GridPaginationTemplateContext> PaginationTemplate { get; set; }

/// <summary>
/// Template for the "load more" button (or other UI element).
/// </summary>
Expand Down Expand Up @@ -523,6 +528,8 @@ private async Task HandleSortingClick(IHxGridColumn<TItem> newSortColumn)
}
}

public Task PagerCurrentPageIndexChanged(int newPageIndex) => HandlePagerCurrentPageIndexChanged(newPageIndex);

private async Task HandlePagerCurrentPageIndexChanged(int newPageIndex)
{
if (await SetCurrentPageIndexWithEventCallback(newPageIndex))
Expand Down
@@ -0,0 +1,47 @@
@inject IDemoDataService DemoDataService

<HxGrid TItem="EmployeeDto" DataProvider="GetGridData" PageSize="_pageSize" Responsive="true">
<Columns>
<HxGridColumn HeaderText="Name" ItemTextSelector="employee => employee.Name" SortKeySelector=" employee => employee.Name" IsDefaultSortColumn="true" />
<HxGridColumn HeaderText="Phone" ItemTextSelector="employee => employee.Phone" SortKeySelector=" employee => employee.Phone" />
<HxGridColumn HeaderText="Salary" ItemTextSelector="@(employee => employee.Salary.ToString("c0"))" SortKeySelector="employee => employee.Salary" />
<HxGridColumn HeaderText="Position" ItemTextSelector="employee => employee.Position" SortKeySelector="employee => employee.Position" />
<HxGridColumn HeaderText="Location" ItemTextSelector="employee => employee.Location" SortKeySelector="employee => employee.Location" />
</Columns>
<PaginationTemplate Context="pagination">
@{
int totalPages = (pagination.TotalCount + pagination.PageSize - 1) / pagination.PageSize;
int firstItemPosition = pagination.CurrentUserState.PageIndex * pagination.PageSize + 1;
int lastItemPosition = Math.Min(firstItemPosition + pagination.PageSize - 1, pagination.TotalCount);
}
<div class="row">
<div class="col d-flex gap-2 align-items-center">
Rows per page: <HxSelect @bind-Value="@_pageSize" Data="_pageSizes" Nullable="false" AutoSort="false" InputSize="InputSize.Small" />
</div>
<div class="col d-flex justify-content-center align-items-center">
Showing @firstItemPosition to @lastItemPosition of @pagination.TotalCount entries
</div>
<div class="col d-flex align-items-center justify-content-end">
<HxButton Icon="BootstrapIcon.ChevronBarLeft" Enabled="pagination.CurrentUserState.PageIndex > 0" OnClick="() => pagination.ChangeCurrentPageIndexAsync(0)" Color="ThemeColor.Link" />
<HxButton Icon="BootstrapIcon.ChevronLeft" Enabled="pagination.CurrentUserState.PageIndex > 0" OnClick="() => pagination.ChangeCurrentPageIndexAsync(pagination.CurrentUserState.PageIndex - 1)" Color="ThemeColor.Link" />
<HxButton Icon="BootstrapIcon.ChevronRight" Enabled="pagination.CurrentUserState.PageIndex + 1 < totalPages" OnClick="() => pagination.ChangeCurrentPageIndexAsync(pagination.CurrentUserState.PageIndex + 1)" Color="ThemeColor.Link" />
<HxButton Icon="BootstrapIcon.ChevronBarRight" Enabled="pagination.CurrentUserState.PageIndex + 1 < totalPages" OnClick="() => pagination.ChangeCurrentPageIndexAsync(totalPages - 1)" Color="ThemeColor.Link" />
</div>
</div>
</PaginationTemplate>
</HxGrid>

@code {
private int _pageSize = 5;
private readonly List<int> _pageSizes = [5, 10, 20];

private async Task<GridDataProviderResult<EmployeeDto>> GetGridData(GridDataProviderRequest<EmployeeDto> request)
{
// you usually pass the data-request to your API/DataLayer
return new GridDataProviderResult<EmployeeDto>()
{
Data = await DemoDataService.GetEmployeesDataFragmentAsync(request.StartIndex, request.Count, request.CancellationToken),
TotalCount = await DemoDataService.GetEmployeesCountAsync(request.CancellationToken)
};
}
}
Expand Up @@ -93,6 +93,12 @@
It's also possible to combine the <i>Load More</i> button with pagination by opting for <code>GridContentNavigationMode.PaginationAndLoadMore</code>.
</p>

<DocHeading Title="Custom paging" Level="3" />
<p>
<code>HxGrid</code> uses the <code>HxPager</code> to display pagination, unless a value for the <code>PaginationTemplate</code> parameter is provided.
Context information is provided via <a href="@($"/types/{nameof(GridPaginationTemplateContext)}")"><code>GridPaginationTemplateContext</code></a> to your pager.
</p>
<Demo Type="typeof(HxGrid_Demo_CustomPagination)" />

<DocHeading Title="Context menu" Id="context-menu" />
<p>
Expand Down
Expand Up @@ -5821,6 +5821,36 @@
We do not use HxGrid because we have HxGrid&lt;TItem&gt;, which leads to GridLoadMoreTemplateContext&lt;TItem&gt;.
</remark>
</member>
<member name="T:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext">
<summary>
Context for the <see cref="P:Havit.Blazor.Components.Web.Bootstrap.HxGrid`1.PaginationTemplate" /> of the <see cref="T:Havit.Blazor.Components.Web.Bootstrap.HxGrid`1"/>.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext.CurrentUserState">
<summary>
Current grid user state (contains <see cref="P:Havit.Blazor.Components.Web.Bootstrap.GridUserState.PageIndex"/>).
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext.PageSize">
<summary>
Items per page.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext.TotalCount">
<summary>
Total number of data items.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext.PagerSettings">
<summary>
Settings for the pager (derived from <c>HxGrid.PagerSettings</c> .. <c>HxGrid.Settings.PagerSettings</c> .. <c>HxGrid.Defaults.PagerSettings</c> cascade).
</summary>
</member>
<member name="M:Havit.Blazor.Components.Web.Bootstrap.GridPaginationTemplateContext.ChangeCurrentPageIndexAsync(System.Int32)">
<summary>
Instructs the grid to change the current page index.
</summary>
</member>
<member name="T:Havit.Blazor.Components.Web.Bootstrap.GridPlaceholderCellContext">
<summary>
Placeholder cell context.
Expand Down Expand Up @@ -6101,6 +6131,11 @@
Template for rendering when the data source is empty but not null.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxGrid`1.PaginationTemplate">
<summary>
Template for rendering custom pagination.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxGrid`1.LoadMoreTemplate">
<summary>
Template for the "load more" button (or other UI element).
Expand Down

0 comments on commit e8216b2

Please sign in to comment.