Row dragging can be combined with Row Grouping. This page focuses on grouping-specific configuration; see the Row Dragging overview for the general capabilities and limitations of the feature.
Managed Row Dragging Copy Link
Managed Row Dragging can update grouped data automatically. The grid moves the dragged rows or groups and updates the underlying row data.
In this example, users can drag athletes and groups of athletes between countries.
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
AutoGroupColumnDef,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GetRowIdFunc,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
NumberEditorModule,
NumberFilterModule,
RowDragModule,
RowSelectionModule,
RowSelectionOptions,
TextEditorModule,
TextFilterModule,
enableDevValidations,
} from "ag-grid-community";
import { BatchEditModule, RowGroupingModule } from "ag-grid-enterprise";
import { getAthletesData } from "./data";
import { IAthlete } from "./types";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
RowGroupingModule,
RowDragModule,
RowSelectionModule,
TextEditorModule,
TextFilterModule,
NumberEditorModule,
NumberFilterModule,
BatchEditModule,
]);
@Component({
selector: "my-app",
standalone: true,
imports: [AgGridAngular],
template: `<ag-grid-angular
style="width: 100%; height: 100%;"
[columnDefs]="columnDefs"
[defaultColDef]="defaultColDef"
[autoGroupColumnDef]="autoGroupColumnDef"
[animateRows]="true"
[groupDefaultExpanded]="groupDefaultExpanded"
[rowDragManaged]="true"
[suppressMoveWhenRowDragging]="true"
[refreshAfterGroupEdit]="true"
[rowDragMultiRow]="true"
[rowSelection]="rowSelection"
[getRowId]="getRowId"
[rowData]="rowData"
/> `,
})
export class AppComponent {
columnDefs: ColDef[] = [
{ field: "country", width: 120, rowGroup: true, editable: true },
{ field: "year", width: 90, rowGroup: true, editable: true },
{ field: "athlete", minWidth: 150 },
{ field: "age", minWidth: 50, filter: "agNumberColumnFilter" },
{ field: "date", width: 110 },
{ field: "sport", width: 110 },
{ field: "gold", width: 110 },
{ field: "silver", width: 110 },
{ field: "bronze", width: 110 },
];
defaultColDef: ColDef = {
sortable: true,
filter: true,
};
autoGroupColumnDef: AutoGroupColumnDef = {
rowDrag: true,
width: 250,
};
groupDefaultExpanded = -1;
rowSelection: RowSelectionOptions | "single" | "multiple" = {
mode: "multiRow",
headerCheckbox: false,
};
getRowId: GetRowIdFunc = ({ data }) => data.id;
rowData: IAthlete[] | null = getAthletesData();
}
import type { IAthlete } from './types';
export const getAthletesData = (): IAthlete[] => {
return [
{
id: '1',
athlete: 'Natalie Coughlin',
age: 25,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 1,
silver: 2,
bronze: 3,
total: 6,
},
{
id: '2',
athlete: 'Aleksey Nemov',
age: 24,
country: null,
year: 2000,
date: null,
sport: 'Gymnastics',
gold: 2,
silver: 1,
bronze: 3,
total: 6,
},
{
id: '3',
athlete: 'Alicia Coutts',
age: 24,
country: 'Australia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
},
{
id: '4',
athlete: 'Missy Franklin',
age: 17,
country: null,
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 4,
silver: 0,
bronze: 1,
total: 5,
},
{
id: '5',
athlete: 'Ryan Lochte',
age: null,
country: 'United States',
year: 2012,
date: null,
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 1,
total: 5,
},
{
id: '6',
athlete: 'Allison Schmitt',
age: 22,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 1,
total: 5,
},
{
id: '7',
athlete: 'Natalie Coughlin',
age: 21,
country: 'United States',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 1,
total: 5,
},
{
id: '8',
athlete: 'Dara Torres',
age: 33,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 3,
total: 5,
},
{
id: '9',
athlete: 'Cindy Klassen',
age: null,
country: 'Canada',
year: 2006,
date: '26/02/2006',
sport: 'Speed Skating',
gold: 1,
silver: 2,
bronze: 2,
total: 5,
},
{
id: '10',
athlete: 'Nastia Liukin',
age: 18,
country: null,
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
},
{
id: '11',
athlete: 'Marit Bjørgen',
age: 29,
country: 'Norway',
year: 2010,
date: null,
sport: 'Cross Country Skiing',
gold: 3,
silver: 1,
bronze: 1,
total: 5,
},
{
id: '12',
athlete: 'Sun Yang',
age: null,
country: 'China',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
id: '13',
athlete: 'Kirsty Coventry',
age: 24,
country: 'Zimbabwe',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
id: '14',
athlete: 'Libby Lenton-Trickett',
age: 23,
country: 'Australia',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
id: '15',
athlete: 'Ryan Lochte',
age: 24,
country: null,
year: 2008,
date: null,
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
},
{
id: '16',
athlete: 'Inge de Bruijn',
age: null,
country: 'Netherlands',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
id: '17',
athlete: 'Petria Thomas',
age: 28,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
id: '18',
athlete: 'Ian Thorpe',
age: 21,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
id: '19',
athlete: 'Inge de Bruijn',
age: 27,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
id: '20',
athlete: 'Gary Hall Jr.',
age: 25,
country: 'United States',
year: 2000,
date: null,
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
id: '21',
athlete: 'Michael Klim',
age: 23,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 0,
total: 4,
},
{
id: '22',
athlete: "Susie O'Neill",
age: 27,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
id: '23',
athlete: 'Jenny Thompson',
age: 27,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
},
{
id: '24',
athlete: 'Pieter van den Hoogenband',
age: 22,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
},
{
id: '25',
athlete: 'An Hyeon-Su',
age: 20,
country: 'South Korea',
year: 2006,
date: '26/02/2006',
sport: 'Short-Track Speed Skating',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
},
{
id: '26',
athlete: 'Aliya Mustafina',
age: 17,
country: 'Russia',
year: 2012,
date: '12/08/2012',
sport: 'Gymnastics',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
id: '27',
athlete: 'Shawn Johnson',
age: 16,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
id: '28',
athlete: 'Dmitry Sautin',
age: 26,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Diving',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
id: '29',
athlete: 'Leontien Zijlaard-van Moorsel',
age: 30,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Cycling',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
id: '30',
athlete: 'Petter Northug Jr.',
age: 24,
country: 'Norway',
year: 2010,
date: '28/02/2010',
sport: 'Cross Country Skiing',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
id: '31',
athlete: 'Ole Einar Bjørndalen',
age: 28,
country: 'Norway',
year: 2002,
date: '24/02/2002',
sport: 'Biathlon',
gold: 4,
silver: 0,
bronze: 0,
total: 4,
},
];
};
export interface IAthlete {
id: string;
athlete: string;
age: number | null;
country: string | null;
year: number;
date: string | null;
sport: string;
gold: number;
silver: number;
bronze: number;
total: number;
}
import '@angular/compiler';
import { provideHttpClient } from '@angular/common/http';
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
if (new URLSearchParams(window.location.search).get('prod') !== 'false') {
enableProdMode();
}
const app = bootstrapApplication(AppComponent, {
providers: [provideHttpClient()],
});
Configuration Copy Link
When using managed row dragging with grouping the following configuration is required:
getRowIdis required to uniquely identify rows.rowDragManaged=trueis needed to enable managed dragging.refreshAfterGroupEdit=trueis required so the grid re-evaluates the groups immediately after a drag changes the grouped column values.rowDrag=trueneeds to be set in a column or in the auto group column definitions or therowDragEntireRow=truein the grid options.suppressMoveWhenRowDragging=trueto avoid the performance penalty of rebuilding the whole hierarchy while dragging and to avoid the grid reordering rows mid-drag, which otherwise causes large visual jumps when parent branches move.BatchEditModuleis recommended, together withTextEditorModule(or your preferred editor modules) and to handle edit and batch edit events.- Managed drag works only with Client-Side Row Model.
See Editing Groups for more on editing grouped data and refreshAfterGroupEdit.
API Reference Copy Link
Set to true to enable Managed Row Dragging. |
Set to true to suppress moving rows while dragging the rowDrag waffle. This option highlights the position where the row will be placed and it will only move the row on mouse up. |
When true, the grid re-evaluates the grouping hierarchy after editing a grouped column value,
moving the row to the correct group instantly. Also enables managed row dragging to update
grouped column values so rows can move between groups. |
Unmanaged Row Dragging with Groups Copy Link
Unmanaged Row Dragging can update the group membership in application code by handling the row drag events and mutating the data that drives the grid. This keeps all row models and grid features available because the grid simply emits events.
The example below shows unmanaged row dragging with Row Grouping where the following can be noted:
- The Athlete column enables a drag handle only for leaf rows via the callback version of
rowDrag. - Because the grid does not manage reordering, dragging remains available even while sorting or filtering is applied.
- The example listens to
onRowDragMoveto change thecountryfor the dragged row in real time, and usesapi.applyTransaction({ update: [...] })to commit the change. - The application can decide whether to update rows during the drag or after
rowDragEnd, and can allow changes regardless of sort and filter state.
import { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import {
ClientSideRowModelApiModule,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
NumberFilterModule,
RowDragCallbackParams,
RowDragEndEvent,
RowDragModule,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ColumnsToolPanelModule,
ContextMenuModule,
RowGroupingModule,
SetFilterModule,
} from "ag-grid-enterprise";
import { getData } from "./data";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
RowDragModule,
ClientSideRowModelApiModule,
NumberFilterModule,
ClientSideRowModelModule,
ColumnsToolPanelModule,
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
SetFilterModule,
]);
@Component({
selector: "my-app",
standalone: true,
imports: [AgGridAngular],
template: `<ag-grid-angular
style="width: 100%; height: 100%;"
[columnDefs]="columnDefs"
[defaultColDef]="defaultColDef"
[groupDefaultExpanded]="groupDefaultExpanded"
[rowData]="rowData"
(rowDragMove)="onRowDragMove($event)"
(gridReady)="onGridReady($event)"
/> `,
})
export class AppComponent {
private gridApi!: GridApi;
columnDefs: ColDef[] = [
{ field: "athlete", rowDrag: rowDrag },
{ field: "country", rowGroup: true },
{ field: "year", width: 100 },
{ field: "date" },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
];
defaultColDef: ColDef = {
width: 170,
filter: true,
};
groupDefaultExpanded = 1;
rowData!: any[];
onRowDragMove(event: RowDragEndEvent) {
const movingNode = event.node!;
const overNode = event.overNode!;
// find out what country group we are hovering over
let groupCountry;
if (overNode.group) {
// if over a group, we take the group key (which will be the
// country as we are grouping by country)
groupCountry = overNode.key;
} else {
// if over a non-group, we take the country directly
groupCountry = overNode.data.country;
}
const needToChangeParent = movingNode.data.country !== groupCountry;
if (needToChangeParent) {
const movingData = movingNode.data;
movingData.country = groupCountry;
this.gridApi.applyTransaction({
update: [movingData],
});
this.gridApi.clearFocusedCell();
}
}
onGridReady(params: GridReadyEvent) {
this.gridApi = params.api;
params.api.setGridOption("rowData", getData());
}
}
const rowDrag = function (params: RowDragCallbackParams) {
// only rows that are NOT groups should be draggable
return !params.node.group;
};
export function getData(): any[] {
return [
{
athlete: 'Aleksey Nemov',
age: 24,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Gymnastics',
gold: 2,
silver: 1,
bronze: 3,
total: 6,
},
{
athlete: 'Alicia Coutts',
age: 24,
country: 'Australia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
},
{
athlete: 'Ian Thorpe',
age: 17,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 2,
bronze: 0,
total: 5,
},
{
athlete: 'Dara Torres',
age: 33,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 3,
total: 5,
},
{
athlete: 'Cindy Klassen',
age: 26,
country: 'Canada',
year: 2006,
date: '26/02/2006',
sport: 'Speed Skating',
gold: 1,
silver: 2,
bronze: 2,
total: 5,
},
{
athlete: 'Nastia Liukin',
age: 18,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 1,
total: 5,
},
{
athlete: 'Marit Bjørgen',
age: 29,
country: 'Norway',
year: 2010,
date: '28/02/2010',
sport: 'Cross Country Skiing',
gold: 3,
silver: 1,
bronze: 1,
total: 5,
},
{
athlete: 'Sun Yang',
age: 20,
country: 'China',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
athlete: 'Kirsty Coventry',
age: 24,
country: 'Zimbabwe',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
athlete: 'Libby Lenton-Trickett',
age: 23,
country: 'Australia',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
athlete: 'Ryan Lochte',
age: 24,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
},
{
athlete: 'Inge de Bruijn',
age: 30,
country: 'Netherlands',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
athlete: 'Petria Thomas',
age: 28,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
athlete: 'Ian Thorpe',
age: 21,
country: 'Australia',
year: 2004,
date: '29/08/2004',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
athlete: 'Inge de Bruijn',
age: 27,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
athlete: 'Gary Hall Jr.',
age: 25,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
athlete: 'Michael Klim',
age: 23,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 2,
bronze: 0,
total: 4,
},
{
athlete: "Susie O'Neill",
age: 27,
country: 'Australia',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
athlete: 'Jenny Thompson',
age: 27,
country: 'United States',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
},
{
athlete: 'Pieter van den Hoogenband',
age: 22,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Swimming',
gold: 2,
silver: 0,
bronze: 2,
total: 4,
},
{
athlete: 'An Hyeon-Su',
age: 20,
country: 'South Korea',
year: 2006,
date: '26/02/2006',
sport: 'Short-Track Speed Skating',
gold: 3,
silver: 0,
bronze: 1,
total: 4,
},
{
athlete: 'Aliya Mustafina',
age: 17,
country: 'Russia',
year: 2012,
date: '12/08/2012',
sport: 'Gymnastics',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
athlete: 'Shawn Johnson',
age: 16,
country: 'United States',
year: 2008,
date: '24/08/2008',
sport: 'Gymnastics',
gold: 1,
silver: 3,
bronze: 0,
total: 4,
},
{
athlete: 'Dmitry Sautin',
age: 26,
country: 'Russia',
year: 2000,
date: '01/10/2000',
sport: 'Diving',
gold: 1,
silver: 1,
bronze: 2,
total: 4,
},
{
athlete: 'Leontien Zijlaard-van Moorsel',
age: 30,
country: 'Netherlands',
year: 2000,
date: '01/10/2000',
sport: 'Cycling',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
athlete: 'Petter Northug Jr.',
age: 24,
country: 'Norway',
year: 2010,
date: '28/02/2010',
sport: 'Cross Country Skiing',
gold: 2,
silver: 1,
bronze: 1,
total: 4,
},
{
athlete: 'Ole Einar Bjørndalen',
age: 28,
country: 'Norway',
year: 2002,
date: '24/02/2002',
sport: 'Biathlon',
gold: 4,
silver: 0,
bronze: 0,
total: 4,
},
{
athlete: 'Janica Kostelic',
age: 20,
country: 'Croatia',
year: 2002,
date: '24/02/2002',
sport: 'Alpine Skiing',
gold: 3,
silver: 1,
bronze: 0,
total: 4,
},
{
athlete: 'Nathan Adrian',
age: 23,
country: 'United States',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 0,
total: 3,
},
{
athlete: 'Yannick Agnel',
age: 20,
country: 'France',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 2,
silver: 1,
bronze: 0,
total: 3,
},
{
athlete: 'Brittany Elmslie',
age: 18,
country: 'Australia',
year: 2012,
date: '12/08/2012',
sport: 'Swimming',
gold: 1,
silver: 2,
bronze: 0,
total: 3,
},
];
}
import '@angular/compiler';
import { provideHttpClient } from '@angular/common/http';
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
if (new URLSearchParams(window.location.search).get('prod') !== 'false') {
enableProdMode();
}
const app = bootstrapApplication(AppComponent, {
providers: [provideHttpClient()],
});