Building on ControlledTable
This is part 2 of a blog post I wrote on getting server side pagination working in the free version of AG React Data Grid. Here, we'll be going over how to add server side sorting to ControlledTable
.
Where'd my Rows Go?
I recently had a debugging session with Milos at Achievers. He was adding sorting to the columns of the ControlledTable
I covered in part one. He added the sortable
property to each of the columns we wanted to sort by. The problem we were having was directly related to our jury rigging of server side pagination out of an AG Grid configuration meant for client side pagination. In AG Grid, making a column sortable gives you the ability to toggle sorting by a column by clicking on that column's header. This was not working. Clicking on the column header was apparently causing all the rows to just disappear. After a few minutes of looking at console logs, Milos got the idea to check the last page of the table. We flipped to the last page, and the rows were all there. What happened?
AG Grid is Trying it's Best
When you tell AG Grid that you want a table with client side pagination that is sorted by one of the columns, it will assume that all the row data is present and attempt to sort it on the client side. You will remember from part one that our row data in the ControlledTable
is padded with "placeholder rows." This is precisely because AG Grid expects all the row data to be present to correctly render a paginated table. Unfortunately, if you attempt to sort with these placeholder rows, all of the placeholder rows will be grouped together and (if in ascending order) will place the placeholder rows before the actual rows. What we actually wanted was for the AG Grid to maintain the sorting configuration in its state while leaving the actual sorting to the server. At the same time, we were telling the AG Grid that we wanted sorting for an ostensibly client side paginated table. The AG Grid had no choice but to try and sort the row data padded with placeholder rows.
Get Sorting Working with ControlledTable With this One Weird Hack
In order to get server side sorting working, we needed a way to get the AG Grid to ignore the actual order of the row data. All we needed to do to accomplish this was to override the comparison function to tell the AG Grid that every row was really the same.
const COLUMN_DEFS = [
{
headerName: "Name",
field: "name",
flex: 2,
+ sortable: true,
+ comparator: () => 0
},
{
headerName: "Value",
field: "value",
flex: 1,
+ sortable: true,
+ comparator: () => 0
}
];
In your implementation of ControlledTable
, you may want to include an override of the comparator
the parent component passes in.
columnDefs = useMemo(() => {
for (const column of columnDefs) {
if (column.sortable) {
column.comparator = () => 0;
}
}
return columnDefs;
}, [columnDefs]);
Other Updates
Another update we needed was the ability to pass in an event handler for when sorting is applied to the table.
function ControlledTable({
...
onPageNumberChange,
+ onSortChanged,
pagination,
...
}) {
...
<AgGridReact
...
onPaginationChanged={onPaginationChanged}
+ onSortChanged={onSortChanged}
getRowNodeId={getRowNodeId}
...
/>
...
}
In the new sandbox version the onSortChanged
callback looks like this:
const onSortChanged = ({ api: { sortController } }) => {
const sortModel = sortController.getSortModel();
if (sortModel.length > 0) {
const { colId, sort } = sortModel[0];
setSortField(colId);
setSortOrder(sort);
} else {
setSortField(null);
setSortOrder(null);
}
setRows([]);
};
api.sortController.getSortModel
returns an array of columns with sorting currently applied to them. Each item has a colId
and sort
property which are the field name and sort order ("asc" or "desc") respectively. I am clearing the row data when the sorting changes because I want the ControlledTable
to trigger a refetch of the data with the new sorting options.
Getting the Loading Overlay Working
One thing I found while creating the new sandbox version for this post was that the loading overlay wasn't showing when the rows were being refetched. I think that passing in the empty row data was somehow causing the AG Grid properties to reset, thereby clearing the overlay that was being programmatically shown in one of the useEffect
s. The solution to this was to encapsulate the contents of that useEffect
within a function and setting the AG Grid's onRowDataChanged
callback to that new function.
const onRowDataChanged = useCallback(() => {
if (!gridApi) {
return;
}
if (loadingBlocks.includes(pageNumber * pageSize)) {
gridApi.showLoadingOverlay();
} else {
gridApi.hideOverlay();
}
}, [gridApi, pageNumber, pageSize, loadingBlocks]);
That's All for Now!
So that's it. We've added server side sorting to our table. Thank you to Milos for working on those original changes. Here again is new sandbox version.