Search code examples
reactjsag-gridag-grid-react

How to implement unmanaged multi-row dragging in ag-grid react?


I've been trying to sort out how to implement multi-row dragging in ag-grid when also using unmanaged row dragging. So far, I have single-row dragging working properly, but cannot seem to access the list of row nodes when dragging multiple.

In the RowDragEvent events that I'm getting in my onRowDragEnter,onRowDragMove, etc. functions, even when selecting multiple and dragging them the RowDragEvent.nodes value is empty, when it should be a list of all moving nodes. Because of this, it shows on the screen as "holding" multiple but only "moving" one, if that makes sense. See below for what I mean.

(it's only dragging Test 23 when it should drag the other two as well) Image of how dragging looks for me at the moment

Could this be related to not using the correct props/settings on my <AgGridReact ... /> component? Or is this feature not currently working with unmanaged dragging?

For context my grid is grouped, and I've added some of my code below

  // https://www.ag-grid.com/react-data-grid/row-dragging/#example-dragging-with-row-groups
  const onRowDragMove = (event: RowDragEvent) => {
    // here's where I want to use event.nodes, but it's always undefined
    !event.node.group &&
      // extra functionality here to save data to our backend
      rowDragUpdateFn(event, (movingData, changedParams) => {
        gridAPI.applyTransaction({
          update: [movingData],
        });
        gridAPI.clearFocusedCell();
      });
  };
  <AgGridReact
  ...
    onRowDragEnter={onRowDragEnter}
    onRowDragEnd={onRowDragEnd}
    onRowDragMove={onRowDragMove}
    rowDragEntireRow
    rowDragMultiRow
    rowSelection={"multiple"}
  ...
  />

Solution

  • Update: March 1, 2022

    Created an issue for this on Github: https://github.com/ag-grid/ag-grid/issues/4932


    This seems like a bug in Ag-grid, still broken in Ag-grid Community 27.0.1. Here is a workaround that determines which rows are being dragged by checking current selection. In this example I am working out the set of dragged rows in onRowDragEnter because you need this information in onRowDragMove. But this can also be done in onRowDragEnd if you only need to know which rows were moved on drag end. That simplifies the code as we don't even need the ref.

    const Workaround: React.FC = () => {
      // Keeps track of dragged rows across all the row drag events.
      // useRef instead of useState to avoid re-rendering.
      const draggedNodes = useRef<RowNode[] | null>();
    
      const onRowDragEnter = (event: RowDragEvent) => {
        // Determine list of dragged nodes whenever drag starts or re-enters the grid.
        // We have to do it in both cases because we may never get drag end event (if
        // drag ends outside the grid).
    
        // There's probably a way to differentiate drag start and drag re-enter, but
        // it doesn't seem worth the effort unless the app can have very large numbers
        // of rows selected at the same time.
    
        // Dragged node
        const { node } = event;
    
        // Nodes currently selected in the grid
        const selectedNodes = event.api.getSelectedNodes();
    
        // If user is dragging one of the selected rows, then they are dragging all the selected rows.
        // I am using checkbox selection, so some rows may be selected, but user is free to drag
        // one of the rows that are not selected. So we check if the dragged node is part of
        // current selection to determine if user is dragging selected rows or a different row.
        if (selectedNodes.length > 1 && selectedNodes.includes(node)) {
          // Dragging all selected rows
          draggedNodes.current = selectedNodes;
        } else {
          // Dragging a single row
          draggedNodes.current = [node];
        }
      };
    
      const onRowDragMove = (event: RowDragEvent) => {
        // Use draggedNodes.current instead of event.nodes
      };
    
      const onRowDragLeave = (event: RowDragEvent) => {
        // Clear ref on leave in case drag ends outside the grid.
        draggedNodes.current = null;
      };
    
      const onRowDragEnd = (event: RowDragEvent) => {
        if (draggedNodes.current) {
          // Do something with draggedNodes.current, then clear ref.
          console.log(draggedNodes.current);
          draggedNodes.current = null;
        }
      };
    
      return (
        <div>
          <AgGridReact
            onRowDragEnter={onRowDragEnter}
            onRowDragMove={onRowDragMove}
            onRowDragLeave={onRowDragLeave}
            onRowDragEnd={onRowDragEnd}
            rowDragMultiRow
          />
        </div>
      );
    };