Data Grid

Paginated table support

Purpose of Component

RfDataGrid adds support for paginated tables that may include filters. This component does not manage filters nor does it process the data but instead requires the developer to hook into a series of events to update the table when needed.

When to Use

When there is a large amount of tabular data that needs filtering, sorting, and pagination support.

Basic Usage

Unlike most Data grids, To use RfDataGrid the columns are broken into three seperate render fragments: Headers, Cells, and Filters. Each render fragement fills in the approriate section of the table. There are several pre built components foreach of these render fragments that can be used.

Each of these sub components communicate with the RfDataGrid via DataGridContext which the RfDataGrid creates and passes down via a CascadingParameter.

Headers

The render fragment Headers is where each columns th elements should go. As an option, if sorting isn't required, you could remove the creation of additional sub components by just writing the standard elements within Headers.

//Example Headers
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column n</th>

Below list out sub components of RfDataGrid

Component Purpose
RfDgHeader Wraps a th providing support for sorting columns.

Filters

Filters render fragment is an optional location to add filters for the DataGrid. They are included in the thead underneath the Headers row. There are several prebuilt filters that can be used or if a custom one is required look to RfDgFilterInput base class for an example of a filter.

However, this class is not required. To make the filter work correctly you will need to have a DataGridContext parameter and properly call DataGridContext.OnFilterChanged, or call the pages listener for RfDataGrid.OnLoad.

Below list out all predefined filters. More are planned to be written.

Component Purpose
RfDgFilterNone Use this if a column doesn't need a filter.
RfDgFilterInputDate A date input filter.
RfDgFilterInputDateTime A date and time input filter.
RfDgFilterInputDateTimeOffset A date and time input filter using DateTimeOffset
RfDgFilterInputDouble A double input filter.
RfDgFilterInputFloat A float input filter.
RfDgFilterInputInt A int input filter.
RfDgFilterInputLong A long input filter.
RfDgFilterInputText A string input filter.
RfDgFilterInputTime A time input filter.
NOTE: To improve performance, the RfDataGrid is not aware of any filters nor does it manage them. Treat this simarly to how EditForm component works. This has the additional benifit to easily support filters outside of the RfDataGrid.

Cells

The Cells render fragment is where you format the content of a row of the table. The Cells has the current row of data being passed into it giving it access to context where context equals to the TRowData.

Unlike Headers needing to use RfDgHeader, the Cells has no interactions with the RfDataGrid. This allows a row to render with no components improving performance.

There are currently no sub components for Cells

Required Parameters

RfDataGrid needs the following parameters set in order to render properly.

  • Data - Without it the skeleton is shown.
  • TotalCount - Needs to show for pagination.
  • CurrentPageIndex - Needs to show for pagination.
  • SortOrder - Required if sorting is allowed.
  • SortKey - Required if sorting is allowed.
  • OnLoadData - Required to setup Data

Example

Below shows an example of setting up the RfDataGrid. The example doesn't show the properties declarations or the OnLoadData function.

<RfDataGrid OnLoadData="@OnLoadData"
            Data=UserCollection
            TotalCount=TotalCount
            @bind-CurrentPageIndex=CurrentPageIndex
            @bind-SortOrder=CurrentSortOrder
            @bind-SortKey=@CurrentSortKey>

    <Headers>
        <RfDgHeader SortKey="Id" >Id</RfDgHeader>
        <RfDgHeader SortKey="FirstName" >First Name</RfDgHeader>
        <RfDgHeader SortKey="LastName" >Last Name</RfDgHeader>
        <RfDgHeader SortKey="Email" >Email</RfDgHeader>
    </Headers>

    <Filters>
        <RfDgFilterNone />
        <RfDgFilterInputText @bind-Value=Filter.FirstName />
        <RfDgFilterInputText @bind-Value=Filter.LastName />
        <RfDgFilterInputText @bind-Value=Filter.Email />
    </Filters>

    <Cells>
        <td>@context.Id</td>
        <td>@context.FirstName</td>
        <td>@context.LastName</td>
        <td>@context.Email</td>
    </Cells>    
</RfDataGrid>

Empty Content

If the Data is empty, the RfDataGrid will render a EmptyContent wrapped in a tr. This is useful to show a message to the user that there is no data to display.

Example

<RfDataGrid OnLoadData="OnLoadData"
            Data="UserCollection"
            TotalCount="TotalCount"
            bind-CurrentPageIndex="CurrentPageIndex"
            bind-SortOrder="CurrentSortOrder"
            bind-SortKey="CurrentSortKey">

    <Headers>
        <RfDgHeader SortKey="Id">Id</RfDgHeader>
        <RfDgHeader SortKey="FirstName">First Name</RfDgHeader>
        <RfDgHeader SortKey="LastName">Last Name</RfDgHeader>
        <RfDgHeader SortKey="Email">Email</RfDgHeader>
    </Headers>

    <Cells>
        <td>@context.Id</td>
        <td>@context.FirstName</td>
        <td>@context.LastName</td>
        <td>@context.Email</td>
    </Cells>

    <EmptyContent>
        <tr>
            <td colspan="4">No data available.</td>
        </tr>
    </EmptyContent>

</RfDataGrid>

Paging on an Empty Grid

TotalPages is derived from TotalCount and PageSize, so a grid with TotalCount=0 has zero pages. In that state the pager renders fully disabled: First and Previous by the guard CurrentPageIndex == 0, and Next and Last by the guard CurrentPageIndex + 1 >= TotalPages. The page tab list is empty, so there is no tab to click into an invalid page either.

The page index is also clamped in code, not just visually: OnNextPageClick and OnLastPageClick never target a negative page. On an empty grid both clamp to page 0, which equals the current page, so CurrentPageIndexChanged does not fire at all. A consumer that feeds the index into a Skip can never receive a negative index from the pager. Paging on non-empty grids is unchanged.

Changing a filter always returns the grid to the first page, because the old page number may no longer exist in the filtered results. CurrentPageIndexChanged(0) fires, and the pager label and the next Next or Last click use page 1 straight away, the same as when the sort changes.

Async Context Events

The DataGridContext raises its notifications as async, multicast events: OnSortChanged (raised by DataGridContext.SortChanged) and OnFilterChanged (raised by DataGridContext.FilterChanged). Any number of components can subscribe to the same event, including the RfDataGrid itself: its own subscription is what resets paging and calls OnLoadData.

Raising one of these events awaits every subscriber before it returns. Subscribers are invoked in registration order; if a handler yields, the later handlers start without waiting for it, and the raiser completes only once all of the handlers' tasks have finished. After a sort click, for example, the event does not finish until both the grid's data reload and every other OnSortChanged handler have completed.

Fault handling: all subscribers always run to completion. If exactly one subscriber faults, its exception is rethrown unchanged to the code that raised the event. If more than one faults, the faults are combined into a single AggregateException containing every subscriber exception. A subscriber that throws before its first await does not stop the others from running.

Example

A component rendered inside the grid can subscribe to the cascaded context. Below, one handler records the sort and a second reloads related data; both complete before the sort event returns.

public partial class SortWatcher : ComponentBase, IDisposable
{
    [CascadingParameter]
    private DataGridContext Context { get; set; }

    private string LastSort { get; set; }
    private List<RelatedItem> Related { get; set; }

    protected override void OnInitialized()
    {
        Context.OnSortChanged += RecordSortAsync;
        Context.OnSortChanged += ReloadRelatedAsync;
    }

    private Task RecordSortAsync(object sender, DataGridSortBy sortBy)
    {
        LastSort = $"{sortBy.SortKey} {sortBy.SortOrder}";
        return Task.CompletedTask;
    }

    // Runs to completion before DataGridContext.SortChanged returns.
    private async Task ReloadRelatedAsync(object sender, DataGridSortBy sortBy)
    {
        Related = await relatedApi.GetAsync(sortBy.SortKey);
        StateHasChanged();
    }

    public void Dispose()
    {
        Context.OnSortChanged -= RecordSortAsync;
        Context.OnSortChanged -= ReloadRelatedAsync;
    }
}

Unsubscribe in Dispose. The context lives as long as the grid, so a forgotten handler stays attached and is awaited on every future event.

How to Setup

Setting up RForge is simple. Follow the below steps to add RForge Blazor components to your project.

  1. Install the Nuget package
  2. Import the namespaces (optional)
  3. Register RForge services
  4. Include Bulma CSS

Install the Nuget Package

RForge Blazor components are installed via Nuget. There are two packages. One for the blazor app and one for the library if any.

Blazor Project Installation

Command Line
dotnet add package RForge.Blazor
Package Manager Console
Install-Package RForge.Blazor

Library Project Installation

This is optional but may be useful if you want to use common enums across the backend and frontend.

Command Line
dotnet add package RForge.Abstractions
Package Manager Console
Install-Package RForge.Abstractions

Import the namespaces

To simplify namespacing while using RForge, the package is designed with most all components sitting within the root RForge namespace.

You may want to include the following namespaces in your _import.razor for ease of use.

@using RForgeBlazor
@using RForgeBlazor.Services
@using RForge.Abstractions
Namespace Purpose
RForgeBlaozr Houses all of the blazor components.
RForgeBlaozr.Services Houses interfaces you may need to communicate with certain components.
RForgeBlaozr.Abstractions Common enums used through out the components.

Register RForge Services

In order for some components to work they must first have their services registered.

Open up Program.cs and add the following line before the var app = builder.Build();.

Heads Up!
If your project is using the multi project solution where one project is server and the other is client. You will need to register the services in both projects.

using RForgeBlazor;
//...
builder.Services.AddRfForgeBlazorServices();
//...
var app = builder.Build();
    

Include Bulma CSS

RForge makes use of Bulma CSS to stylize the components. Add / install Bulma and add the CSS file to the head. Link to download: Bulma releases.

An unhandled error has occurred. Reload 🗙