Pagination is not working properly in Blazor FluentDataGrid - Stack Overflow

admin2025-04-29  3

I am trying to display data using FluentDataGrid but I am getting very strange behavior. Issues:

  1. The total item count of the grid is not updated properly. That is why pagination buttons are not working.
  2. I somehow manage to set the total items count, but then when I change the page, the event always gets 0 as page number.

Here is my code. I am using @rendermode = InteractiveAssembly.

@rendermode InteractiveWebAssembly

@inject HttpClientService httpClientService

<FluentDataGrid Items="RolesData?.Data?.AsQueryable()"
                TGridItem="GetRoles.Response"
                Pagination="paginationState"
                ShowHover="true"
                MultiLine="true"
                Loading="@gridLoader">
    <PropertyColumn Property="@(c => c.Name)" Sortable="false" />
    <TemplateColumn Title="Permissions">
        <p>@context.PermissionCount permissions assigned</p>
    </TemplateColumn>
    <TemplateColumn Title="Assigned To">
        <p>@context.AssignedTo uers</p>
    </TemplateColumn>
    <TemplateColumn Title="Actions">
        @if (context.IsPrimary == false)
        {
            <FluentButton aria-label="Edit item" IconEnd="@(new Icons.Regular.Size16.Edit())" OnClick="() => OnEditClick(context.Id)" />
            <FluentButton aria-label="Delete item" IconEnd="@(new Icons.Regular.Size16.Delete())" OnClick="() => OnDeleteClick(context.Id)" />
        }
    </TemplateColumn>
</FluentDataGrid>
<FluentPaginator State="@paginationState" CurrentPageIndexChanged="OnCurrentPageIndexChanged" />

That is my component. I am fetching data on OnAfterRenderAsync initially

private GetRoles.Request request = new GetRoles.Request();
private Pagination.Response<IEnumerable<GetRoles.Response>>? RolesData;

PaginationState paginationState = new PaginationState { ItemsPerPage = 10 };
bool gridLoader;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await GetRoles();
    }
}

private async Task GetRoles()
{
    gridLoader = true;
    StateHasChanged();

    request.Page = paginationState.CurrentPageIndex;
    request.PageSize = paginationState.ItemsPerPage;

    var roleDataResponse = await httpClientService.GetAsync<Pagination.Response<IEnumerable<GetRoles.Response>>>("/api/roles", request);
    if (roleDataResponse != null)
    {
        RolesData = roleDataResponse;

        await paginationState.SetTotalItemCountAsync(roleDataResponse.TotalRecords);
    }

    gridLoader = false;
    StateHasChanged();

    Console.WriteLine($"CurrentPageIndex: {paginationState.CurrentPageIndex}");
    Console.WriteLine($"LastPageIndex: {paginationState.LastPageIndex}");
    Console.WriteLine($"TotalItemCount: {paginationState.TotalItemCount}");
}

The event which is handling the button click on the pagination is

async void OnCurrentPageIndexChanged(int index)
{
    await paginationState.SetCurrentPageIndexAsync(index);
    await GetRoles();
}

This is the data returned by the API

{
    "totalRecords": 11,
    "data": [
        {
            "id": "Dy9dkGzW",
            "name": "Test 9",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "eV9ablPg",
            "name": "Test 8",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "X892olBV",
            "name": "Test 7",
            "permissionCount": 10,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "8e7V896p",
            "name": "Test 6",
            "permissionCount": 1,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "1VGx2l5Q",
            "name": "Test 5",
            "permissionCount": 4,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "5Y7Qq7Bk",
            "name": "Test 4",
            "permissionCount": 9,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "O8l1wGqx",
            "name": "Test 3",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "Y8Gb39Bq",
            "name": "Test 2",
            "permissionCount": 3,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "QY7BV7OV",
            "name": "Test 1",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "ZYG8o71v",
            "name": "Finance",
            "permissionCount": 8,
            "assignedTo": 0,
            "isPrimary": false
        }
    ]
}

As you can see the 'totalRecords' are 11 here. The pagination should show total records as 11 and it should have 2 pages, but it is showing me this

If I move 'await paginationState.SetTotalItemCountAsync(roleDataResponse.TotalRecords);' to the end of the method, it sets the total items count correctly and the pagination buttons are not disabled, but when I click on the next button, the event handler get 0 in 'index'.

Am I missing something here or doing something really wrong? Any help will be appreciated.

Thank you in advance.

I am trying to display data using FluentDataGrid but I am getting very strange behavior. Issues:

  1. The total item count of the grid is not updated properly. That is why pagination buttons are not working.
  2. I somehow manage to set the total items count, but then when I change the page, the event always gets 0 as page number.

Here is my code. I am using @rendermode = InteractiveAssembly.

@rendermode InteractiveWebAssembly

@inject HttpClientService httpClientService

<FluentDataGrid Items="RolesData?.Data?.AsQueryable()"
                TGridItem="GetRoles.Response"
                Pagination="paginationState"
                ShowHover="true"
                MultiLine="true"
                Loading="@gridLoader">
    <PropertyColumn Property="@(c => c.Name)" Sortable="false" />
    <TemplateColumn Title="Permissions">
        <p>@context.PermissionCount permissions assigned</p>
    </TemplateColumn>
    <TemplateColumn Title="Assigned To">
        <p>@context.AssignedTo uers</p>
    </TemplateColumn>
    <TemplateColumn Title="Actions">
        @if (context.IsPrimary == false)
        {
            <FluentButton aria-label="Edit item" IconEnd="@(new Icons.Regular.Size16.Edit())" OnClick="() => OnEditClick(context.Id)" />
            <FluentButton aria-label="Delete item" IconEnd="@(new Icons.Regular.Size16.Delete())" OnClick="() => OnDeleteClick(context.Id)" />
        }
    </TemplateColumn>
</FluentDataGrid>
<FluentPaginator State="@paginationState" CurrentPageIndexChanged="OnCurrentPageIndexChanged" />

That is my component. I am fetching data on OnAfterRenderAsync initially

private GetRoles.Request request = new GetRoles.Request();
private Pagination.Response<IEnumerable<GetRoles.Response>>? RolesData;

PaginationState paginationState = new PaginationState { ItemsPerPage = 10 };
bool gridLoader;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await GetRoles();
    }
}

private async Task GetRoles()
{
    gridLoader = true;
    StateHasChanged();

    request.Page = paginationState.CurrentPageIndex;
    request.PageSize = paginationState.ItemsPerPage;

    var roleDataResponse = await httpClientService.GetAsync<Pagination.Response<IEnumerable<GetRoles.Response>>>("/api/roles", request);
    if (roleDataResponse != null)
    {
        RolesData = roleDataResponse;

        await paginationState.SetTotalItemCountAsync(roleDataResponse.TotalRecords);
    }

    gridLoader = false;
    StateHasChanged();

    Console.WriteLine($"CurrentPageIndex: {paginationState.CurrentPageIndex}");
    Console.WriteLine($"LastPageIndex: {paginationState.LastPageIndex}");
    Console.WriteLine($"TotalItemCount: {paginationState.TotalItemCount}");
}

The event which is handling the button click on the pagination is

async void OnCurrentPageIndexChanged(int index)
{
    await paginationState.SetCurrentPageIndexAsync(index);
    await GetRoles();
}

This is the data returned by the API

{
    "totalRecords": 11,
    "data": [
        {
            "id": "Dy9dkGzW",
            "name": "Test 9",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "eV9ablPg",
            "name": "Test 8",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "X892olBV",
            "name": "Test 7",
            "permissionCount": 10,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "8e7V896p",
            "name": "Test 6",
            "permissionCount": 1,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "1VGx2l5Q",
            "name": "Test 5",
            "permissionCount": 4,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "5Y7Qq7Bk",
            "name": "Test 4",
            "permissionCount": 9,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "O8l1wGqx",
            "name": "Test 3",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "Y8Gb39Bq",
            "name": "Test 2",
            "permissionCount": 3,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "QY7BV7OV",
            "name": "Test 1",
            "permissionCount": 2,
            "assignedTo": 0,
            "isPrimary": false
        },
        {
            "id": "ZYG8o71v",
            "name": "Finance",
            "permissionCount": 8,
            "assignedTo": 0,
            "isPrimary": false
        }
    ]
}

As you can see the 'totalRecords' are 11 here. The pagination should show total records as 11 and it should have 2 pages, but it is showing me this

If I move 'await paginationState.SetTotalItemCountAsync(roleDataResponse.TotalRecords);' to the end of the method, it sets the total items count correctly and the pagination buttons are not disabled, but when I click on the next button, the event handler get 0 in 'index'.

Am I missing something here or doing something really wrong? Any help will be appreciated.

Thank you in advance.

Share Improve this question asked Jan 8 at 13:04 DrXSsiveDrXSsive 1851 silver badge21 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 1

You're trying to plumb this up the wrong way. You need to use the ItemsProvider, and then everything gets much simpler.

As I'm not sure of your data object [and not about to try and re-invent it], I've simplified everything by substituting in the good old WeatherForecast.

Here's the page code:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new Fluent Blazor app.
<FluentDataGrid TGridItem="WeatherForecast"
                Pagination="_paginationState"
                ShowHover="true"
                MultiLine="true"
                ItemsProvider="GetDataAsync">

    <PropertyColumn Title="Date" Property="@(c => c!.Date)" Sortable="true" Align=Align.Start />
    <PropertyColumn Title="Temp. (C)" Property="@(c => c!.TemperatureC)" Sortable="true" Align=Align.Center />
    <PropertyColumn Title="Temp. (F)" Property="@(c => c!.TemperatureF)" Sortable="true" Align=Align.Center />
    <PropertyColumn Title="Summary" Property="@(c => c!.Summary)" Sortable="true" Align=Align.End />

</FluentDataGrid>

<FluentPaginator State="@_paginationState" />

@code {
    private PaginationState _paginationState = new PaginationState { ItemsPerPage = 10 };

    private async ValueTask<GridItemsProviderResult<WeatherForecast>> GetDataAsync(GridItemsProviderRequest<WeatherForecast> request)
    {
        // Map the UI GridItemsProviderRequest to the data pipeline list request object
        var listRequest = new ListRequest(request.StartIndex, request.Count ?? _paginationState.ItemsPerPage);

        var result = await WeatherForecastProvider.GetAPIDataAsync(listRequest);

        // Map the data pipeline result to the UI GridItemsProviderResult
        return GridItemsProviderResult.From<WeatherForecast>(result.Items.ToList(), result.TotalRecords);
    }
}

And the supporting objects:

    // Stub Class to mimic getting data from an external source
    public static class WeatherForecastProvider
    {
        private static List<WeatherForecast> _weatherForecasts = new();

        public static async ValueTask<ListResult<WeatherForecast>> GetAPIDataAsync(ListRequest request)
        {
            // Fake as asynbc data get
            await Task.Yield();

            _weatherForecasts = _weatherForecasts ?? LoadData();

            var list = _weatherForecasts
                .Skip(request.StartIndex)
                .Take(request.PageSize)
                .ToList();

            return new ListResult<WeatherForecast>(list, _weatherForecasts.Count());
        }

        private static List<WeatherForecast> LoadData()
        {
            var startDate = DateOnly.FromDateTime(DateTime.Now);
            var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
            return Enumerable.Range(1, 50).Select(index => new WeatherForecast
                {
                    Date = startDate.AddDays(index),
                    TemperatureC = Random.Shared.Next(-20, 55),
                    Summary = summaries[Random.Shared.Next(summaries.Length)]
                }).ToList();
        }
    }

    // The data pipeline request and result objects
    public readonly record struct ListRequest(int StartIndex, int PageSize);
    public readonly record struct ListResult<TRecord>(IEnumerable<TRecord> Items, int TotalRecords);

    public class WeatherForecast
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
转载请注明原文地址:http://anycun.com/QandA/1745856375a91288.html