Use MudBlazor MudDataGrid ServerData to load paged data from an API
Asked Answered
A

2

10

In my opinion the documentation for MudDataGrid is a bit lacking when it comes to loading paged data from a dynamic source such as an API.

  • How do I use the ServerData attribute?
  • How do I handle row clicks?
  • How do I send additional filter / search criteria on the API call?
  • How do I pre-set the Page and PageSize of the GridState object?
Androsphinx answered 30/8, 2023 at 12:50 Comment(0)
A
31

Let's say you want to make a blazor page that has a list of animals on it, displayed in a MudDataGrid. The animal data will come from an API.

How do I use the ServerData attribute?

Firstly, define DTOs to handle the user's request, and the response from the API:

public class GridDataRequestDto
{
    public int Page { get; set; } = 0; // The page number for the data we're requesting
    public int PageSize { get; set; } = 10; // The number of items per page
}

public class AnimalListDto
{
    public List<AnimalListItemDto> Items { get; set; } = new();
    public int ItemTotalCount { get; set; } = 0; // The total count of items before paging
}

public class AnimalListItemDto
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

This, then, is the implementation of your MudDataGrid component in your blazor page:

<MudDataGrid ServerData="LoadGridData"
   T="AnimalListItemDto" @ref="_dataGrid">
    <Columns>
        <PropertyColumn Property="@(item => item.Name)" Title="Animal" />
     </Columns>
    <PagerContent>
        <MudDataGridPager T="AnimalListItemDto" />
    </PagerContent>
</MudDataGrid>

As you can see, the Grid's ServerData attribute is set to call a method called LoadGridData. We define that in our blazor page code:

private MudDataGrid<AnimalListItemDto>? _dataGrid;
private GridDataRequestDto _requestDto = new();

private async Task<GridData<AnimalListItemDto>> LoadGridData(GridState<AnimalListItemDto> state)
{
    _requestDto.Page = state.Page;
    _requestDto.PageSize = state.PageSize;

    AnimalListDto apiResponse = await GetAnimalList(_requestDto);
    GridData<AnimalListItemDto> data = new()
        {
            Items = apiResponse.Items,
            TotalItems = apiResponse.ItemTotalCount
        };

    return data;
}

You need to create the GetAnimalList() method that performs the API call, so on the server you would do your DB query and return a AnimalListDto result with the Items and ItemTotalCount properties populated.

After you've done that, congratulations! You have successfully implemented ServerData in MudDataGrid.

How do I handle row clicks?

Say you want the user to view an animal when they click on a MudDataGrid row. Also you want to put a button on each row to allow the user to edit the animal if they click it.

Let's modify the MudDataGrid implementation in the blazor page code a little:

<MudDataGrid ServerData="LoadGridData" T="AnimalListItemDto" 
    RowClick="OnRowClick" Hover="true" @ref="_dataGrid">
    <Columns>
        <PropertyColumn Property="@(item => item.Name)" Title="Animal" />
        <TemplateColumn>
            <CellTemplate>
                <button @onclick="() => EditItem(context.Item!)" @onclick:stopPropagation>Edit</button>
            </CellTemplate>
        </TemplateColumn>
     </Columns>
    <PagerContent>
        <MudDataGridPager T="AnimalListItemDto" />
    </PagerContent>
</MudDataGrid>

So now we have to implement a couple of new methods in our code:

private async Task OnRowClick(DataGridRowClickEventArgs<AnimalListItemDto> args)
{
    YourViewMethod(args.Item);
}

private void EditItem(AnimalListItemDto item)
{
    YourEditMethod(item);
}

How do I send additional filter / search criteria on the API call?

Now we want the user to be able to search the Animal data, by putting keywords into a search box.

Firstly we need to add a SearchTerm property to our request DTO:

public class GridDataRequestDto
{
    public string? SearchTerm { get; set; } = null;
    public int Page { get; set; } = 0; // The page number for the data we're requesting
    public int PageSize { get; set; } = 10; // The number of items per page
}

Then, we add a form to the Blazor page, above the grid:

<EditForm Model="_requestDto" OnValidSubmit="Search">
    <InputText placeholder="e.g. Animal Name" @bind-Value="_requestDto.SearchTerm" />
    <button type="submit">Search</button>
</EditForm>

Now we add the Search method:

private async Task Search()
{
    if (_dataGrid is not null)
    {
        await _dataGrid!.ReloadServerData();
    }
}

Now the SearchTerm property is naturally sent along to the API call. You just need to modify your DB query to handle it.

How do I pre-set the Page and PageSize of the GridState object?

This might be a niche requirement, but you might want to use persisted state to pre-set the data grid to load a different page of data to the default. You might want to do this if the user has left the page, and then returned, expecting the data to be on the page where they left it.

Firstly, you need to load the state into the request DTO. So, you need to implement a state manager, e.g. an instance of type GridDataRequestDto called AnimalListState that you inject into your page by adding a scoped MyStateHelper service to your Program.cs builder.Services. Then in your blazor page code:

@inject IMyStateHelper MyStateHelper;

protected override async Task OnParametersSetAsync()
{
    _requestDto = MyStateHelper.AnimalListState;
}

That way, the request DTO has been pre-populated when it comes to render time. So the final step is to to tell the DataGrid to pre-set the Page and PageSize. MudBlazor haven't given us a very nice way to do it, but I find that this approach works:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender && _dataGrid?.HasPager == true)
    {
        _dataGrid!.CurrentPage = _requestDto.Page;
        await _dataGrid.SetRowsPerPageAsync(_requestDto.PageSize);
    }
}

That's all folks. Please let me know if you have any problems.

Androsphinx answered 30/8, 2023 at 12:50 Comment(5)
How to filter and sort using state.FilterDefinitions and state.SortDefinitions on the server, I mean in radzen blazor we simply write: query = query.Where(args.Filter); and filter is encoded as a string that linq IQueryiable understands. what to do this with MudBlazor on the server using the mentioned properties?Burlingame
Super! I have already one question. Can it work with virtualization? No pagination just load data when user scroll up or down, please?Velda
Would love to see this comment updated with how to handle SortDefinitions. Documentation is very unclear on that as well.Entrain
what a fantastic answer ! The documentation for this is woefully lacking (criminally so) this helped me so much and enabled me to solve the very same issue, I use serverside services to get the paginated data and this worked. I did have one problem though, the buttons to step though pages are always disable, I am reporting the correct row total and the correct number of pages, but the buttons are always disabled. Is there anything else that needs to be done to activate them ? (I am unable to actually move through all the pages)Lynellelynett
If I inspect the html, the buttons are always marked as disabled, ive defined the control like this <PagerContent> <MudDataGridPager T="AgencyGridViewModel" Disabled="false"/> </PagerContent>Lynellelynett
S
3

Updating Search method like this will force the current page back to 0.

if (_dataGrid is not null)
{
  if (_dataGrid!.CurrentPage != 0)
    _dataGrid!.CurrentPage = 0; // Reset the current page back to 0 will force LoadData
  else
    await _dataGrid!.ReloadServerData();
}
Susurrate answered 21/1 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.