Row Drag and Drop in Angular Grid Component
17 Sep 202524 minutes to read
The Syncfusion Angular Grid component provides comprehensive row drag and drop functionality, enabling intuitive data manipulation through visual interaction. This feature allows users to rearrange rows within the same grid, transfer rows between different grids, or drag rows to custom components. Row drag and drop enhances user experience by providing direct manipulation capabilities for data organization and workflow management.
Key Capabilities:
- Reorder rows within the same grid
 - Transfer rows between multiple grid instances
 - Drag rows to custom Angular components
 - Customize drag behavior with events and settings
 - Hide drag icons for seamless interaction
 
To implement row drag and drop functionality, inject the RowDDService in the provider section of the AppModule. The RowDDService handles all drag and drop operations within the grid component. After injecting the service, configure the feature using the allowRowDragAndDrop and targetID properties.
Drag and drop within grid
Row reordering within a single grid enables users to reorganize data by dragging rows to new positions using a drag icon. Enable this feature by setting the allowRowDragAndDrop property to true. This boolean property controls the availability of row drag and drop functionality and defaults to false.
The following example demonstrates how to enable drag and drop within the Grid:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule, PageService, SelectionService, RowDDService } from '@syncfusion/ej2-angular-grids'
import { Component, OnInit } from '@angular/core';
import { orderDetails } from './datasource';
import { SelectionSettingsModel } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [
        
        GridModule
    ],
providers: [PageService, SelectionService, RowDDService],
standalone: true,
    selector: 'app-root',
    template: `<div class="control-section">
                <ejs-grid [dataSource]='data' [allowRowDragAndDrop]='true'
                [selectionSettings]='selectionOptions' height='400'>
                    <e-columns>
                        <e-column field='OrderID' headerText='Order ID' width='120'
                        textAlign='Right'></e-column>
                        <e-column field='CustomerID' headerText='Customer ID' width='120'>
                        </e-column>
                        <e-column field='OrderDate' headerText='Order Date' width='100' 
                        format="yMd" textAlign='Right'></e-column>
                        <e-column field='Freight' headerText='Freight' width='120' format='C2' 
                        textAlign='Right'></e-column>
                        <e-column field='ShipCity' headerText='Ship City' width='130'>
                        </e-column>
                        <e-column field='ShipCountry' headerText='Ship Country' width='130'>
                        </e-column>
                    </e-columns>
                </ejs-grid>
              </div>`
  })
export class AppComponent implements OnInit {
    public data?: object[];
    public selectionOptions?: SelectionSettingsModel;
    ngOnInit(): void {
        this.data = orderDetails;
        this.selectionOptions = { type: 'Multiple' };
    }
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Drag and drop to grid
The grid supports dragging rows from one grid instance and dropping them into another grid, facilitating data transfer between related datasets. Enable this functionality by setting the allowRowDragAndDrop property to true in both grid components. This property defaults to false, requiring explicit activation.
Specify the destination grid using the targetID property within the rowDropSettings configuration object. The targetID property accepts the DOM element ID of the target grid component.
The following example demonstrates row drag and drop between two Grid components:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule, PageService, SelectionService, RowDDService } from '@syncfusion/ej2-angular-grids'
import { Component, OnInit } from '@angular/core';
import { data } from './datasource';
import { RowDropSettingsModel, SelectionSettingsModel } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [
        
        GridModule
    ],
providers: [PageService, SelectionService, RowDDService],
standalone: true,
    selector: 'app-root',
    template: `<ejs-grid id='Grid' [dataSource]='data' [allowRowDragAndDrop]='true'
                [rowDropSettings]='rowDropOptions' [selectionSettings]='selectionOptions'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' 
                    width=120></e-column>
                    <e-column field='CustomerID' headerText='Customer ID' width=150>
                    </e-column>
                    <e-column field='ShipCity' headerText='Ship City' width=150>
                    </e-column>
                    <e-column field='ShipName' headerText='Ship Name' width=150>
                    </e-column>
                </e-columns>
                </ejs-grid>
                <ejs-grid id='DestGrid' [dataSource]='destGridData' 
                [allowRowDragAndDrop]='true' [rowDropSettings]='destRowDropOptions' 
                [selectionSettings]='selectionOptions'>
                <e-columns>
                    <e-column field='OrderID' headerText='Order ID' textAlign='Right' 
                    width=120></e-column>
                    <e-column field='CustomerID' headerText='Customer ID' width=150>
                    </e-column>
                    <e-column field='ShipCity' headerText='Ship City' width=150></e-column>
                    <e-column field='ShipName' headerText='Ship Name' width=150></e-column>
                </e-columns>
                </ejs-grid>`,
    styleUrls: ['./app.style.css']
})
export class AppComponent implements OnInit {
    public data?: object[];
    public destGridData?: object[];
    public rowDropOptions?: RowDropSettingsModel;
    public destRowDropOptions?: RowDropSettingsModel;
    public selectionOptions?: SelectionSettingsModel;
    ngOnInit(): void {
        this.data = data;
        this.destGridData = [];
        this.rowDropOptions = { targetID: 'DestGrid' };
        this.destRowDropOptions = { targetID: 'Grid' };
        this.selectionOptions = { type: 'Multiple' };
    }
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
- Row drag and drop functionality is not supported with virtual scrolling and frozen rows and columns mode.
 - The RowDDService must be injected in the provider section of AppModule to enable row drag and drop features.
 
Drag and drop to custom component
The Grid enables dragging rows to any custom Angular component, providing flexibility for complex data manipulation scenarios. This capability supports workflows where data moves between different UI components or applications. Enable this feature by setting the allowRowDragAndDrop property to true and specifying the custom component’s ID in the targetID property of the rowDropSettings object. The targetID must match the DOM element ID of the target component.
In the following example, selected grid rows are dragged and dropped into a TreeView component using the rowDrop event. Upon successful drop, the corresponding grid row is removed and its data is inserted into the custom component:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule, PageService, SelectionService, RowDDService } from '@syncfusion/ej2-angular-grids'
import { TreeGridAllModule } from '@syncfusion/ej2-angular-treegrid'
import { Component, ViewChild,OnInit } from '@angular/core';
import { sampleGridData } from './datasource';
import { isNullOrUndefined } from '@syncfusion/ej2-base';
import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { GridComponent, RowDragEventArgs } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [
        
        GridModule,
        TreeGridAllModule
    ],
providers: [PageService, SelectionService, RowDDService],
standalone: true,
  selector: 'app-root',
  template: ` <ejs-grid #grid id='Grid' [dataSource]='sourceData' [allowPaging]="true"
              [pageSettings]="true" [allowSelection]="true" [allowRowDragAndDrop]="true" 
              [selectionSettings]="selectionOptions" [rowDropSettings]="srcDropOptions" 
              (rowDrop)='onRowDrop($event)' [editSettings]="gridEditSettings">
                <e-columns> 
                        <e-column field='taskID' headerText='Task ID' textAlign='Right' 
                        width=90></e-column>
                        <e-column field='taskName' headerText='Task Name' textAlign='Left' 
                        width=180></e-column>
                        <e-column field='description' headerText='Description' 
                        textAlign='Left' width=180></e-column>
                        <e-column field='category' headerText='Category' 
                        textAlign='Left' width=180></e-column>
                        <e-column field='startDate' headerText='Start Date' textAlign='Right' 
                        format='yMd' width=120></e-column>
                        <e-column field='duration' headerText='Duration' textAlign='Right' 
                        width=80></e-column>
                </e-columns>
              </ejs-grid>
              <ejs-treegrid #treegrid id='TreeGrid'  
              childMapping='subtasks' [editSettings]='treeGridEditSettings'>
                  <e-columns>
                      <e-column field='taskID' headerText='Task ID'  textAlign='Right' 
                      width=90></e-column>
                      <e-column field='taskName' headerText='Task Name' textAlign='Left' 
                      width=180></e-column>
                      <e-column field='startDate' headerText='Start Date' textAlign='Right' 
                      format='yMd' width=90></e-column>
                      <e-column field='duration' headerText='Duration' textAlign='Right' 
                      width=80></e-column>
                </e-columns>
              </ejs-treegrid>`
})
export class AppComponent implements OnInit {
  @ViewChild('grid')
  gridObject!: GridComponent;
  @ViewChild('treegrid')
  treeGridObject!: TreeGridComponent;
  public sourceData: Object[] = [];
  public selectionOptions?: Object;
  public srcDropOptions?: Object;
  public treegridData: object[] = [];
  public gridEditSettings?:Object;
  public treeGridEditSettings?:Object;
  ngOnInit(): void {
    this.sourceData = sampleGridData;
    this.selectionOptions = { type: 'Multiple' };
    this.srcDropOptions = { targetID: 'TreeGrid' };
    this.gridEditSettings = { allowDeleting: true };
    this.treeGridEditSettings = { allowAdding: true, allowEditing: true };
  }
  onRowDrop(args:RowDragEventArgs) {
    if ((args.target as Element).closest('.e-treegrid')) {
      args.cancel = true;
      var rowIndex = !isNullOrUndefined((args.target as Element).closest('.e-row') as Element)
        ? ((args.target as HTMLElement).closest('.e-row') as any).rowIndex
        : 0;
      for (var i = 0; i < (args.data as Object[]).length; i++) {
        this.treeGridObject.addRecord((args.data as Object[])[i] , rowIndex);
        this.gridObject.deleteRecord('taskID', (args.data as Object[])[i]);
      }
    }
  }
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
- The
 rowDropevent triggers when a row is dropped onto a custom component, regardless of drop success. Use theargs.cancelproperty to prevent the default action and implement custom handling logic.
Drag and drop rows without drag icons
For a cleaner interface, the Grid can perform drag and drop operations without displaying drag icons. This approach provides seamless row reordering through direct row interaction. Achieve this by setting the targetID property of the rowDropSettings object to the current Grid’s ID.
When the targetID matches the Grid’s own ID, the Grid renders without helper icons for row dragging. Customize the drag and drop behavior by handling the rowDrop event. Within the event handler, prevent the default action by setting args.cancel to true, then reorder rows using the reorderRows method.
The following example demonstrates how to implement drag and drop without icons:
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule,SelectionService, RowDDService } from '@syncfusion/ej2-angular-grids'
import { GridComponent, RowDragEventArgs } from '@syncfusion/ej2-angular-grids';
import { data } from './datasource';
import { Component, ViewChild } from '@angular/core';
@Component({
    imports: [ GridModule],
    providers: [SelectionService, RowDDService],
    standalone: true,
    selector: 'app-root',
    template: `<div class="control-section">
                <ejs-grid #grid [dataSource]="data" [allowRowDragAndDrop]="true"
                [selectionSettings]="selectOptions" [rowDropSettings]="rowDropOptions"
                id="Grid" (rowDrop)="rowDrop($event)">
                    <e-columns>
                        <e-column field="OrderID" headerText="Order ID" width="120"
                        textAlign="Right"></e-column>
                        <e-column field="CustomerID" headerText="Customer Name" 
                        width="130"></e-column>
                        <e-column field="OrderDate" headerText="Order Date" width="120"
                        format="yMd" textAlign="Right"></e-column>
                        <e-column field="Freight" headerText="Freight" width="120"
                        format="C2" textAlign="Right"></e-column>
                        <e-column field="ShipCity" headerText="Ship City" width="130">
                        </e-column>
                        <e-column field="ShipCountry" headerText="Ship Country"
                        width="130"></e-column>
                    </e-columns>
                </ejs-grid>
            </div>`
    })
export class AppComponent {
    public data: Object[] = [];
    public selectOptions?: Object;
    public rowDropOptions?: object;
    @ViewChild('grid') public grid?: GridComponent;
    ngOnInit(): void {
        this.data = data;
        this.selectOptions = { type: 'Multiple' };
        this.rowDropOptions = { targetID: 'Grid' };
    }
    rowDrop(args:RowDragEventArgs) {
        args.cancel = true;
        var value = [];
        for (var r = 0; r < (args.rows as Element[]).length; r++) {
            value.push((args.fromIndex as number) + r);
        }
        (this.grid as GridComponent).reorderRows(value, (args.dropIndex as number));
    }
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
- The selection feature must be enabled to allow users to select rows before performing drag and drop operations.
 - For multiple row selection, set the type property to Multiple. Users can then select multiple rows by clicking and dragging within the grid.
 
Drag and drop events
The Grid component provides comprehensive event handling for drag and drop operations, enabling customization of drag behavior, progress tracking, and drop actions. These events offer granular control over the drag and drop workflow:
- 
    
rowDragStartHelper: Triggers when clicking on the drag icon or grid row, allowing customization of the drag element based on specific criteria.
 - 
    
rowDragStart: Triggers when grid row dragging begins, providing opportunity to initialize drag-specific logic.
 - 
    
rowDrag: Triggers continuously while the grid row is being dragged, enabling real-time feedback and validation.
 - 
    
rowDrop: Triggers when a drag element is dropped onto a target element, allowing custom drop handling and data manipulation.
 
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule, SelectionService, RowDDService } from '@syncfusion/ej2-angular-grids'
import { Component, ViewChild,OnInit } from '@angular/core';
import { GridComponent, RowDragEventArgs } from '@syncfusion/ej2-angular-grids';
import { orderDetails, columnDataType } from './datasource';
@Component({
  imports: [ GridModule],
  providers: [SelectionService, RowDDService],
  standalone: true,
  selector: 'app-root',
  template: `<div style="text-align:center">
             <p style="color:red;" id="message">{{ message }}</p>
             </div>
            <ejs-grid #grid [dataSource]="data" [allowPaging]="true" 
            [allowRowDragAndDrop]="true" (rowDragStartHelper)="rowDragStartHelper($event)"
            [selectionSettings]="selectOptions" 
            id="Grid" (rowDrop)="rowDrop($event)"  (rowDragStart)="rowDragStart($event)" 
            (rowDrag)="rowDrag($event)">
              <e-columns>
                <e-column field="OrderID" headerText="Order ID" width="120"
                textAlign="Right"></e-column>
                <e-column field="CustomerID" headerText="Customer Name" 
                width="130"></e-column>
                <e-column field="OrderDate" headerText="Order Date" width="120"
                format="yMd" textAlign="Right"></e-column>
                <e-column field="Freight" headerText="Freight" width="120"
                format="C2" textAlign="Right"></e-column>
                <e-column field="ShipCity" headerText="Ship City" width="120">
                </e-column>
                <e-column field="ShipCountry" headerText="Ship Country"
                width="120"></e-column>
              </e-columns>
            </ejs-grid>`,
  styleUrls: ['./app.style.css']
})
export class AppComponent implements OnInit {
  public data: Object[] = [];
  public selectOptions?: Object;
  public message?: string;
  @ViewChild('grid')
  public grid!: GridComponent;
  ngOnInit(): void {
    this.data = orderDetails;
    this.selectOptions = { type: 'Multiple' };
  }
  rowDragStartHelper(args: RowDragEventArgs): void {
    this.message = `rowDragStartHelper event triggered`;
    if (((args.data as Object[])[0] as columnDataType).OrderID === 10248) {
      args.cancel = true;
    }
  }
  rowDragStart(args: RowDragEventArgs) {
    this.message = `rowDragStart event triggered`;
    args.cancel = true;
  }
  rowDrag(args:RowDragEventArgs): void {
    this.message = `rowDrag event triggered`;
    (args.rows as Element[]).forEach((row: Element) => {
      row .classList.add('drag-limit');
    });
  }
  rowDrop(args: RowDragEventArgs): void {
    this.message = `rowDrop event triggered`;
    const value = [];
    for (let r = 0; r < (args.rows as Element[]).length; r++) {
      value.push((args.fromIndex as number) + r);
    }
    this.grid.reorderRows(value, (args.dropIndex as number));
  }
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Limitations
- Single row drag and drop is supported within the same grid without enabling row selection.
 - Row drag and drop functionality does not have built-in support with row template, detail template, and hierarchy grid features.
 
See also
Sorting data in the Syncfusion Grid
Filtering data in the Syncfusion Grid