In managed row dragging, the grid is responsible for rearranging the rows as the rows are dragged. Managed dragging is enabled with the property rowDragManaged=true.
The example below shows simple managed dragging. The following can be noted:
- The first column has
rowDrag=truewhich results in a draggable area being included in the cell. - The property
rowDragManagedis set, to tell the grid to move the row as the row is dragged. - If a sort (click on the header) or filter (open up the column menu) is applied to the column, the draggable icon for row dragging is disabled. This is consistent with the constraints explained after the example.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
RowDragModule,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
RowDragModule,
ClientSideRowModelModule,
];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", rowDrag: true },
{ field: "country" },
{ field: "year", width: 100 },
{ field: "date" },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
filter: true,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowDragManaged={true}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; The logic for managed dragging is simple and has the following constraints:
- Works with Client-Side row model only; not with the Infinite, Server-Side or Viewport row models.
- Does not work if Pagination is enabled.
- Does not work when sorting is applied. This is because the sort order of the rows depends on the data and moving the data would break the sort order.
- Does not work when filtering is applied. This is because a filter removes rows making it impossible to know the 'real' order of rows when some are missing.
These constraints can be bypassed by using Unmanaged Row Dragging.
See also Row Dragging with Row Groups for Grouping, that supports both managed and unmanaged row dragging.
See also Row Dragging with Tree Data for Tree Data, that supports both managed and unmanaged row dragging.
See also Row Drag Events.
Suppress Move When Dragging Copy Link
By default, the managed row dragging moves the rows while you are dragging them. This effect might not be desirable due to your application design. To prevent this default behaviour, set suppressMoveWhenRowDragging to true in the gridOptions.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
RowDragModule,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
RowDragModule,
ClientSideRowModelModule,
];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", rowDrag: true },
{ field: "country" },
{ field: "year", width: 100 },
{ field: "date" },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
filter: true,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowDragManaged={true}
suppressMoveWhenRowDragging={true}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; Multi-Row Dragging Copy Link
It is possible to drag multiple rows at the same time, when rowDragMultiRow is set to true in the gridOptions and it is combined with rowSelection.mode='multiRow'.
For this example note the following:
- When you select multiple items and drag one of them, all items in the selection will be dragged.
- When you drag an item that is not selected while other items are selected, only the unselected item will be dragged.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
RowDragModule,
RowSelectionModule,
RowSelectionOptions,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
RowDragModule,
RowSelectionModule,
ClientSideRowModelModule,
];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", rowDrag: true },
{ field: "country" },
{ field: "year", width: 100 },
{ field: "date" },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
filter: true,
};
}, []);
const rowSelection = useMemo<
RowSelectionOptions | "single" | "multiple"
>(() => {
return { mode: "multiRow", headerCheckbox: false };
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowDragManaged={true}
rowDragMultiRow={true}
rowSelection={rowSelection}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; Suppress Row Drag Copy Link
You can hide the draggable area by setting the grid option suppressRowDrag = true.
The example below is almost identical to the Managed Dragging example with the following differences:
- The Suppress button will hide the drag icons.
- The Remove Suppress button will un-hide the drag icons.
- Applying a sort or a filter or entering pivot mode will disable the drag icons.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./styles.css";
import {
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
NumberFilterModule,
RowDragModule,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [
TextFilterModule,
NumberFilterModule,
RowDragModule,
ClientSideRowModelModule,
];
const GridExample = () => {
const gridRef = useRef<AgGridReact<IOlympicData>>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", rowDrag: true },
{ field: "country" },
{ field: "year", width: 100 },
{ field: "date" },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
width: 170,
filter: true,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
const onBtSuppressRowDrag = useCallback(() => {
gridRef.current!.api.setGridOption("suppressRowDrag", true);
}, []);
const onBtShowRowDrag = useCallback(() => {
gridRef.current!.api.setGridOption("suppressRowDrag", false);
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div style={{ marginBottom: "1rem" }}>
<button onClick={onBtSuppressRowDrag}>Suppress</button>
<button onClick={onBtShowRowDrag}>Remove Suppress</button>
</div>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
ref={gridRef}
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
rowDragManaged={true}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; Preventing Dropping on Certain Rows Copy Link
The isRowValidDropPosition callback allows you to control whether a row drop is allowed during managed or unmanaged row dragging, and optionally override the rows, parent or position for the drop. This is useful for restricting where rows can be dropped or customizing drop behaviour. Returning an object allows instead to filter the rows to drop, or change the parent or the position of the drop.
This affects also the icon and label shown when dragging a row for both managed and unmanaged row dragging.
Called by drag and drop when rows are dragged over another row to conditionally prevent dropping the dragged row on the hovered row.
The user can cancel the drop by returning false or customize the operation by returning a IsRowValidDropPositionResult. |