You can use the DataGrid in Telerik UI for Blazor with any data source you want just by leveraging the OnRead event.
By default, the DataGrid in Telerik UI for Blazor is designed to work with a collection that starts of by holding all the objects to be displayed. However, by leveraging the DataGrid’s OnRead event, you can retrieve data on an “as you need it” basis, fetching objects only as the user pages forward through the grid.
There are, at least, three scenarios when you want don’t want to retrieve all of your data up front:
- When there is so much data that retrieving it all would result in unacceptably long start-up times
- When users typically find what they’re looking for in the first few pages that the grid displays (these first two scenarios overlap)
- When reducing the impact on the client’s memory is critical you can achieve all these goals using the Telerik UI for Blazor DataGrid's OnRead method.
For this case study, I’m going to assume the existence of a method that lets me retrieve more data from my data source (I’ve called that method GetMoreData). That method must be passed a start point that the data source will use to retrieve the “next” set of data—in this case study, I’m going to pass the number of items already retrieved. In real life, you may need to pass something else (for example, the key of the last item retrieved from the data source). My GetMoreData method also must be passed the number of objects to be retrieved.
In addition, as I retrieve data from the data source, I’m going to add that data to a collection held at the client. I have two reasons for that, the first being that, as you’ll see, it simplifies my code. However, keeping the fetched data also makes for both a more efficient application and a more responsive application because I don’t have to refetch data as the user pages back.
It does mean that, as the user pages back, they are looking at stale data—refetching the data would give the user the latest version of the data from the data source. However, it’s not clear to me whether that would provide a better user experience: The user might find that having the data change as they page back is more disconcerting than helpful (especially if data has been added or deleted at the data source because that would cause the data to shift from one page to another in the grid).
Configuring the Grid
The first step in setting up the grid for “as you need it” retrieval is to tie the to some field declared as a List, tied to the object type you’re displaying (in my case, I’ll be displaying Employee objects). In the following code, I’ve called that collection pageData. I’m also going to need access to the grid itself, so I set its @ref attribute to another field that I’ll set up (that field must be declared as TelerikGrid, tied to the type of object that the grid is displaying). I’ve also tied the grid’s PageSize property to an int field (which I called PageSize). To fetch data as the user pages forward, you must assign the grid’s OnRead event to some method (I’ve called my method fetchingRows) and the grid’s TotalCount property to another int field (which I’ve called totalCount).
That makes my TelerikGrid declaration look like this:
<TelerikGrid Data="@pageData" Pageable="true" PageSize="@pageSize"
In my code, those required fields look like this:
List<Employee> pageData = new List<Employee>();
int pageSize = 10;
int totalCount = 0;
I also need three other fields. First, I need a collection to hold all the data that I’ve already fetched (I’m calling that collection empData). I also need to specify how many items I’ll fetch (I’m holding that in a field called fetchSize). Finally, I need to keep track of when I’ve pulled all the data from the data source so I also set up a Boolean field called EOF.
Here are those declarations:
List<Employee> empData = new List<Employee>();
int fetchSize = 20;
bool EOF = false;
There is a relationship between fetchSize and pageSize: fetchSize must be some multiple of pageSize (including just being equal to pageSize).
Fetching Data: Getting the Data
You’re now ready to start fetching data “as you need it” by adding code to your OnRead method. The OnRead method is called when the grid is first displayed, whenever the user pages forward or backward, and whenever the grid is updated.
Your first step in the OnRead method is to determine the number of items you have to skip over to get the first item on the new page. Normally, you’d be able to just read the grid’s Page property to determine the current page but, the first time that the OnRead method is called, the grid isn’t available. So, for the first page, the number of items to skip is zero, but for any other page, it’s the page count times the page size:
void fetchingRows(GridReadEventArgs args)
if (theGrid == null)
skip = 0;
skip = (theGrid.Page - 1) * pageSize;
With that number in hand, I can check to see if there’s enough data in my “already fetched” collection to support displaying the page (I also check my EOF field to see if I’ve reached the end of the data source—more on that later). If I do need more data, I retrieve it into a collection called fetchedData and append that to the end of my “already fetched” collection:
if (empData.Count() <= (skip + pageSize) && !EOF)
fetchedData = GetMoreData(empData.Count(), fetchSize + 1);
If you look closely, you can see that I actually fetch one more item than I need by passing fetchSize + 1 as the second parameter to GetMoreData. I’m doing that to determine if I’ve reached the end of my data source—I’m checking to see if there’s at least one more item to support the user paging forward again. If you have some other way of determining if you’re “out of data” (if, for example, the data source returns some EOF marker or you know the total number of items available), then you won’t need to retrieve that “extra” item.
Fetching Data: Supporting Paging
In fact, there’s a lot to be said for knowing, in advance, the number of items available from the data source. When the grid thinks it’s displayed all of the available items, it automatically disables its page-forward buttons. The grid makes that decision by comparing the number of items in the collection it’s displaying to the value in its TotalCount property. If you can retrieve the total number of items in the data source, you can just set the totalCount property to that value and everything will work fine.
I’m going to assume, because I’m trying to speed up my initial display time, that I don’t want to make an “extra” call to determine the total number of items. Instead, most of the time, I’ll just lie to the grid about the number of items to be displayed by setting the TotalCount property to one more than the number of items I’ve retrieved. To do that, I just set the TotalCount field that I bound to the grid’s TotalCount property to one more than the number of items in my “already fetched” collection.
However, once I’ve determined that I’ve exhausted my data source, I’ll give up the pretense and set the TotalCount property to the count of my “already fetched” collection. When that happens, I’ll also set my EOF field to prevent going back to the data source once I’ve retrieved all the data. Here’s the next part of my fetchingRows method with that code:
if (fetchedData.Count() < fetchSize + 1)
EOF = true;
totalCount = empData.Count();
totalCount = empData.Count() + 1;
As I said, keeping that “already fetched” collection around not only provides a better user experience, it also simplifies my code. Without that collection, I’d have to main the totalCount field myself as the maximum number of items I’ve retrieved plus one.
The last step in the OnRead method is to update the collection that the grid is bound to (pageData, in my case) with a selection from the “already fetched” collection. That code is very simple:
pageData = empData.Skip(skip).Take(pageSize).ToList();
And you’re done… provided you don’t want to support adding, deleting, and updating rows in the grid. Once you take control of the OnRead event, you also have to provide methods for some combination of the OnDelete, OnCreate, and OnUpdate events plus a method to support adding a new, blank row. It’s not a lot of code and I’ve covered it elsewhere so I won’t drag you through it again.
But even ignoring the updates, you’ve now got the opportunity to provide your user with a faster grid and better UX than if you fetched all your data up front. And faster is always better.