Managed SSRM selection state with AG Grid
Edit: This article was written prior to the release of AG Grid v29.2.0. Users on v29.2.0 or newer are encouraged to use setServerSideSelectionState instead.
AG Grid is a performant, feature-rich Javascript grid available for use with a variety of modern frontend frameworks. Its relative ease of use makes AG Grid an easy choice when selecting a traditional data grid, making large and complex data sets manageable and interactive to users with little developer overhead.
But managing large data sets comes at a cost. When your data set becomes too large, you can no longer afford to send the entirety of a user’s data to them at one time, as this can become quite slow and forces users to wait for long periods of time while data is being queried or is in transit. To solve this, AG Grid offers an alternative to their default row model known as SSRM (server-side row model). Instead of requiring all of the rows at once, SSRM allows you to provide AG Grid with a datasource which will be used to fetch more data as it’s needed (ie as pagination changes or the user scrolls to the end of a partial list). SSRM allows developers to fetch their data in pages, splitting data into predictably sized chunks and allowing for data retrieval at a speed more or less unaffected by the size of the data set. You can read more about it here.
Side note: Migrating to SSRM is not always an easy undertaking and I recommend it be done as early as possible if you expect your data set to grow.
SSRM feature parity is good — it feels like a first-class addition — but one issue in particular that caused me quite a bit of grief was SSRM’s lack of a suitable selection state. SSRM uses a limited version of the CSRM (client-side row model, default) selection state, relying on having all rows in memory to select out-of-view rows/select all. SSRM errors when trying to selectAll
and selecting unloaded rows is not possible in this model because the grid does not have an unloaded row’s data. The grid I was recently migrating from CSRM to SSRM allowed operations on the entirety of the data set, and that was a feature we could not lose when migrating to SSRM.
So how can we make grids using SSRM work with select all? The answer that worked for us was to extract selection state and manage it ourselves. But what should that state look like? Here’s what we landed on (in Typescript):
1type NodeId = string | number;2type ManagedSelectionState = {3 selectedNodeIds: NodeId[];4 deselectedNodeIds: null;5} | {6 selectedNodeIds: null;7 deselectedNodeIds: NodeId[]8};
At any point, our managed selection state either has a list of selected node IDs or deselected node IDs (node IDs can be provided by the developer).
- If the selected node ID list is non-null, it means the user (or your application via the APIs exposed by AG Grid) selected a some nodes manually (if the list is empty, there are no nodes selected).
- If the deselected node ID list is non-null, it means the user “selected all,” and if there are any elements in the list, it means the user deselected a few.
From this shape, we are able to maintain a selection state compatible with SSRM. It is able to select nodes that have not yet been loaded, or select all nodes in the grid (without needing to know the node ID of each node to select).
Overwriting our selection state onto AG Grid’s selection state (to manage UI updates) is trivial, with a few gotchas:
Whenever pagination changes (ie the user navigates to a new page of data) (there are events exposed for this), we iterated over all nodes in memory and set selection state within AG Grid according to our new managed selection state. We should also subscribe to events in which a user manually selects a checkbox so we can update our managed selection state.
Be careful not to accidentally listen to events fired as a result of enforcing your own selection state. AG Grid exposes a suppressFinishActions
parameter on setSelected
(the API method I used to enforce our managed selection state) which we can use to distinguish which selection events were initiated by the user and which events were caused by a sync with our managed selection state. So, to manage manual user selection, you’ll be working off the selectionChanged
event (suppressed with setSelected
). Unfortunately, that means we need to calculate which rows actually changed. It is a trivial task, but needs to be done manually. To achieve this, iterate over all nodes in memory and keep a list of which nodes were selected. Compare it to your managed state to see what changed, and update your managed state. Be sure to append any unloaded nodes back onto your new state, as these wont show up iterating over nodes in memory. Since they are unloaded, it is safe to assume they were unchanged by the triggering event.
This approach feels a bit hacky at times (namely when suppressing finish actions mentioned above) but this functionality is not supported, and it was the best way we found to flip who manages selection state to make AG Grid fit our needs.