---
title: "Row Dragging Between Grids"
framework: vue
version: "36.1.0"
---

# Row Dragging Between Grids

Row Drag Between Grids is concerned with seamless integration among different grids, allowing records to be dragged from one grid and dropped at a specific index on another grid.

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `addRowDropZone` | `Function` |  |  | Adds a drop zone outside of the grid where rows can be dropped. Module: [`RowDragModule`](https://www.ag-grid.com/vue-data-grid/modules/). |
| `removeRowDropZone` | `Function` |  |  | Removes an external drop zone added by `addRowDropZone`. Module: [`RowDragModule`](https://www.ag-grid.com/vue-data-grid/modules/). |
| `getRowDropZoneParams` | `Function` |  |  | Returns the `RowDropZoneParams` to be used by another grid's `addRowDropZone` method. Module: [`RowDragModule`](https://www.ag-grid.com/vue-data-grid/modules/). |

## Adding a Grid as Target

To allow adding a grid as DropZone, the `getRowDropZoneParams` API method should be used in the target grid and the `addRowDropZone` in the source grid.

```js
const dropZoneParams = targetGridApi.getRowDropZoneParams({
    onDragStop: function() {
        alert('Record Dropped!');
    }
});

if (dropZoneParams) {
    sourceGridApi.addRowDropZone(dropZoneParams);

    // when the DropZone above is no longer needed
    sourceGridApi.removeRowDropZone(dropZoneParams);
}
```

In the example below, note the following:

- When you drag from one grid to another, a line will appear indicating where the row will be placed.
- Rows can be dragged from one grid to the other grid. When the row is received, it is **not** removed from the first grid. This is the choice of the example. The example could equally have removed from the other grid.
- Rows can be removed from both grids by dragging the row to the 'Trash' drop zone.
- New rows can be created by clicking on the red, green and blue buttons.

#### Two Grids with Drop Position

```ts
import { createApp, defineComponent } from "vue";

import type {
  ColDef,
  GetRowIdParams,
  GridApi,
  GridReadyEvent,
  RowClassRules,
  RowDropZoneParams,
} from "ag-grid-community";
import {
  ClientSideRowModelApiModule,
  ClientSideRowModelModule,
  ModuleRegistry,
  RowApiModule,
  RowDragModule,
  RowStyleModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { AgGridVue } from "ag-grid-vue3";

import "./styles.css";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowDragModule,
  ClientSideRowModelApiModule,
  RowApiModule,
  TextFilterModule,
  RowStyleModule,
  ClientSideRowModelModule,
]);

let rowIdSequence = 100;

function createDataItem(color) {
  const obj = {
    id: rowIdSequence++,
    color: color,
    value1: Math.floor(window.agRandom() * 100),
    value2: Math.floor(window.agRandom() * 100),
  };

  return obj;
}

const createRowBlock = (blocks) =>
  Array.apply(null, Array(blocks || 1))
    .map(() => ["Red", "Green", "Blue"].map((color) => createDataItem(color)))
    .reduce((prev, curr) => prev.concat(curr), []);

const VueExample = defineComponent({
  /* html */
  template: `<div class="example-wrapper">

            <div class="inner-col">
                <div class="toolbar">
                    <button class="factory factory-red" data-color="Red" data-side="left" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Red
                    </button>
                    <button class="factory factory-green" data-color="Green" data-side="left" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Green
                    </button>
                    <button class="factory factory-blue" data-color="Blue" data-side="left" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Blue
                    </button>
                </div>
                <div style="height: 100%;" class="inner-col" ref="eLeftGrid">
                    <ag-grid-vue
                        style="height: 100%;"
                        :defaultColDef="defaultColDef"
                        :getRowId="getRowId"
                        :rowClassRules="rowClassRules"
                        :rowDragManaged="true"
                        :suppressMoveWhenRowDragging="true"
                        :rowData="leftRowData"
                        :columnDefs="columns"
                        @grid-ready="onGridReady($event, 'Left')"
                        >
                    </ag-grid-vue>
                </div>
            </div>

            <div class="inner-col vertical-toolbar">
                <span class="bin" ref="eBin">
                    <i class="far fa-trash-alt fa-3x" ref="eBinIcon"></i>
                </span>
            </div>

            <div class="inner-col">
                <div class="toolbar">
                    <button class="factory factory-red" data-color="Red" data-side="right" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Red
                    </button>
                    <button class="factory factory-green" data-color="Green" data-side="right" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Green
                    </button>
                    <button class="factory factory-blue" data-color="Blue" data-side="right" @click="onFactoryButtonClick($event)">
                        <i class="far fa-plus-square"></i>Add Blue
                    </button>
                </div>
                <div style="height: 100%;" class="inner-col" ref="eRightGrid">
                    <ag-grid-vue
                        style="height: 100%;"
                        :defaultColDef="defaultColDef"
                        :getRowId="getRowId"
                        :rowClassRules="rowClassRules"
                        :rowDragManaged="true"
                        :suppressMoveWhenRowDragging="true"
                        :rowData="rightRowData"
                        :columnDefs="columns"
                        @grid-ready="onGridReady($event, 'Right')"
                        >
                    </ag-grid-vue>
                </div>
            </div>
        </div>
    `,
  components: {
    "ag-grid-vue": AgGridVue,
  },
  data: function () {
    return {
      leftRowData: [],
      rightRowData: [],
      leftApi: null,
      rightApi: null,
      rowClassRules: <RowClassRules>{
        "red-row": 'data.color == "Red"',
        "green-row": 'data.color == "Green"',
        "blue-row": 'data.color == "Blue"',
      },
      defaultColDef: <ColDef>{
        flex: 1,
        minWidth: 100,
        filter: true,
      },
      columns: <ColDef[]>[
        { field: "id", rowDrag: true },
        { field: "color" },
        { field: "value1" },
        { field: "value2" },
      ],
    };
  },
  beforeMount() {
    this.leftRowData = createRowBlock(2);
    this.rightRowData = createRowBlock(2);
  },
  methods: {
    getRowId(params: GetRowIdParams) {
      return String(params.data.id);
    },

    onGridReady(params: GridReadyEvent, side: number) {
      const api = params.api;
      if (side === "Left") {
        this.leftApi = api;
      } else {
        this.rightApi = api;
      }

      if (this.leftApi && this.rightApi) {
        this.addBinZone(this.leftApi);
        this.addBinZone(this.rightApi);
        this.addGridDropZone("Left", this.leftApi);
        this.addGridDropZone("Right", this.rightApi);
      }
    },

    addRecordToGrid(side, data) {
      // if data missing or data has no it, do nothing
      if (!data || data.id == null) {
        return;
      }

      const api = side === "left" ? this.leftApi : this.rightApi;
      // do nothing if row is already in the grid, otherwise we would have duplicates
      const rowAlreadyInGrid = !!api.getRowNode(data.id);
      let transaction;

      if (rowAlreadyInGrid) {
        console.log("not adding row to avoid duplicates in the grid");
        return;
      }

      transaction = {
        add: [data],
      };

      api.applyTransaction(transaction);
    },

    onFactoryButtonClick(e) {
      var button = e.currentTarget,
        buttonColor = button.getAttribute("data-color"),
        side = button.getAttribute("data-side"),
        data = createDataItem(buttonColor);

      this.addRecordToGrid(side, data);
    },

    binDrop(data) {
      // if data missing or data has no id, do nothing
      if (!data || data.id == null) {
        return;
      }

      var transaction = {
        remove: [data],
      };

      [this.leftApi, this.rightApi].forEach((api) => {
        var rowsInGrid = !!api.getRowNode(data.id);

        if (rowsInGrid) {
          api.applyTransaction(transaction);
        }
      });
    },

    addBinZone(api: GridApi) {
      const dropZone = <RowDropZoneParams>{
        getContainer: () => this.$refs.eBinIcon,
        onDragEnter: () => {
          this.$refs.eBin.style.color = "blue";
          this.$refs.eBinIcon.style.transform = "scale(1.5)";
        },
        onDragLeave: () => {
          this.$refs.eBin.style = "";
          this.$refs.eBinIcon.style.transform = "scale(1)";
        },
        onDragStop: (params) => {
          this.binDrop(params.node.data);
          this.$refs.eBin.style = "";
          this.$refs.eBinIcon.style.transform = "scale(1)";
        },
      };

      api.addRowDropZone(dropZone);
    },

    addGridDropZone(side, api) {
      const dropApi = side === "Left" ? this.rightApi : this.leftApi;
      const dropZone = dropApi.getRowDropZoneParams();

      api.addRowDropZone(dropZone);
    },
  },
});

createApp(VueExample).mount("#app");
```

[Live example: Two Grids with Drop Position](https://www.ag-grid.com/examples/row-dragging-to-grid/two-grids-with-drop-position/vue3)

## Dragging Multiple Records Between Grids

It is possible to drag multiple records at once from one grid to another.

In the example below, note the following:

- This example enables [Multi-Row Dragging](https://www.ag-grid.com/vue-data-grid/row-dragging-managed/#multi-row-dragging) between grids using `rowDragMultiRow`.
- When `Remove Source Rows` is selected, the rows will be removed from the **Athletes** grid once they are dropped onto the **Selected Athletes** grid.
- If `Only Deselect Source Rows` is selected, all selected rows that were copied will be deselected but will not be removed.
   Note: If some rows are selected and a row that isn't selected is copied, the selected rows will remain selected.
- If `None` is selected, the rows will be copied from one grid to another and the source grid will stay as is.

#### Multiple Records with Drop Position

```ts
import { createApp, defineComponent } from "vue";

import type {
  ColDef,
  GetRowIdParams,
  GridApi,
  GridReadyEvent,
  ICellRendererParams,
  RowDropZoneParams,
  RowSelectionOptions,
} from "ag-grid-community";
import {
  ClientSideRowModelApiModule,
  ClientSideRowModelModule,
  ModuleRegistry,
  RowDragModule,
  RowSelectionModule,
  TextFilterModule,
  enableDevValidations,
} from "ag-grid-community";
import { AgGridVue } from "ag-grid-vue3";

import "./styles.css";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowDragModule,
  ClientSideRowModelApiModule,
  TextFilterModule,
  RowSelectionModule,
  ClientSideRowModelModule,
]);

const SportRenderer = defineComponent({
  template: `<i class="far fa-trash-alt" style="cursor: pointer" @click="applyTransaction()"></i>`,
  methods: {
    applyTransaction() {
      this.params.api.applyTransaction({ remove: [this.params.node.data] });
    },
  },
});

const VueExample = defineComponent({
  template: /* html */ `
        <div class="top-container">
            <div class="example-toolbar panel panel-default">
                <div class="panel-body">
                    <input type="radio" id="move" name="radio" checked ref="eMoveRadio">
                    <label for="move">Remove Source Rows</label>
                    <input type="radio" id="deselect" name="radio" ref="eDeselectRadio">
                    <label for="deselect">Only Deselect Source Rows</label>
                    <input type="radio" id="none" name="radio">
                    <label for="none">None</label>
                    <span class="input-group-button">
                        <button type="button" class="btn btn-default reset" style="margin-left: 5px;" @click="reset()">
                            <i class="fas fa-redo" style="margin-right: 5px;"></i>Reset
                        </button>
                    </span>
                </div>
            </div>
            <div class="grid-wrapper">
                <div class="panel panel-primary" style="margin-right: 10px;">
                    <div class="panel-heading">Athletes</div>
                    <div class="panel-body">
                        <ag-grid-vue
                                style="height: 100%;"
                                :defaultColDef="defaultColDef"
                                :rowSelection="rowSelection"
                                :rowDragMultiRow="true"
                                :getRowId="getRowId"
                                :rowDragManaged="true"
                                :suppressMoveWhenRowDragging="true"
                                :rowData="leftRowData"
                                :columnDefs="leftColumns"
                                @grid-ready="onGridReady($event, 0)"
                                >
                        </ag-grid-vue>
                    </div>
                </div>
                <div class="panel panel-primary" style="margin-left: 10px;">
                    <div class="panel-heading">Selected Athletes</div>
                    <div class="panel-body">
                        <ag-grid-vue
                                style="height: 100%;"
                                :defaultColDef="defaultColDef"
                                :getRowId="getRowId"
                                :rowDragManaged="true"
                                :rowData="rightRowData"
                                :columnDefs="rightColumns"
                                @grid-ready="onGridReady($event, 1)"
                                >
                        </ag-grid-vue>
                    </div>
                </div>
            </div>
        </div>`,
  components: {
    "ag-grid-vue": AgGridVue,
    SportRenderer,
  },
  data: function () {
    return {
      leftRowData: null,
      rightRowData: [],
      rawData: [],
      leftApi: null,
      rightApi: null,

      defaultColDef: <ColDef>{
        flex: 1,
        minWidth: 100,
        filter: true,
      },
      rowSelection: <RowSelectionOptions>{
        mode: "multiRow",
      },
      leftColumns: <ColDef[]>[
        {
          rowDrag: true,
          maxWidth: 50,
          suppressHeaderMenuButton: true,
          suppressHeaderFilterButton: true,
          rowDragText: (params, dragItemCount) => {
            if (dragItemCount > 1) {
              return dragItemCount + " athletes";
            }
            return params.rowNode.data.athlete;
          },
        },
        { field: "athlete" },
        { field: "sport" },
      ],
      rightColumns: <ColDef[]>[
        {
          rowDrag: true,
          maxWidth: 50,
          suppressHeaderMenuButton: true,
          suppressHeaderFilterButton: true,
          rowDragText: (params, dragItemCount) => {
            if (dragItemCount > 1) {
              return dragItemCount + " athletes";
            }
            return params.rowNode.data.athlete;
          },
        },
        { field: "athlete" },
        { field: "sport" },
        {
          suppressHeaderMenuButton: true,
          suppressHeaderFilterButton: true,
          maxWidth: 50,
          cellRenderer: "SportRenderer",
        },
      ],
    };
  },
  beforeMount() {
    fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
      .then((resp) => resp.json())
      .then((data) => {
        const athletes = [];
        let i = 0;

        while (athletes.length < 20 && i < data.length) {
          var pos = i++;
          if (athletes.some((rec) => rec.athlete === data[pos].athlete)) {
            continue;
          }
          athletes.push(data[pos]);
        }
        this.rawData = athletes;
        this.loadGrids();
      });
  },
  methods: {
    getRowId(params: GetRowIdParams) {
      return params.data.athlete;
    },

    loadGrids() {
      this.leftRowData = [...this.rawData];
      this.rightRowData = [];
    },

    reset() {
      this.$refs.eMoveRadio.checked = true;

      this.loadGrids();
    },

    onGridReady(params: GridReadyEvent, side: number) {
      if (side === 0) {
        this.leftApi = params.api;
      }

      if (side === 1) {
        this.rightApi = params.api;
        this.addGridDropZone();
      }
    },

    addGridDropZone() {
      const dropZoneParams = <RowDropZoneParams>(
        this.rightApi.getRowDropZoneParams({
          onDragStop: (params) => {
            var deselectCheck = this.$refs.eDeselectRadio.checked;
            var moveCheck = this.$refs.eMoveRadio.checked;
            var nodes = params.nodes;

            if (moveCheck) {
              this.leftApi.applyTransaction({
                remove: nodes.map(function (node) {
                  return node.data;
                }),
              });
            } else if (deselectCheck) {
              this.leftApi.setNodesSelected({ nodes, newValue: false });
            }
          },
        })
      );

      this.leftApi.addRowDropZone(dropZoneParams);
    },
  },
});

createApp(VueExample).mount("#app");
```

[Live example: Multiple Records with Drop Position](https://www.ag-grid.com/examples/row-dragging-to-grid/two-grids-with-multiple-records/vue3)
