How to Use Drag and Drop in Angular?

Hello Guys, This is my third article here we are learning how to drag and drop table in Angular.

Let' Start

Step 1. Create component <ng g c componentname>

Step 2. Install drage and drop from npm js  npm i angular-drag-drop.

Step 3. Add code in the your HTML file.

<mat-table [dataSource]="dataSource" class="mat-elevation-z8" cdkDropListGroup>

  <ng-container *ngFor="let column of columns; let i = index" [matColumnDef]="column.columnDef">

    <mat-header-cell *matHeaderCellDef cdkDropList cdkDropListLockAxis="x" cdkDropListOrientation="horizontal"
      (cdkDropListDropped)="dropListDropped($event, i, column.columnDef)" cdkDrag
      (cdkDragStarted)="dragStarted($event, i)" [cdkDragData]="{name: column.columnDef, columIndex: i}">

      <mat-select placeholder="-- Select --" [value]="column.columnDef"
        (selectionChange)="switchColumn($event,column.columnDef)">

        <mat-option *ngIf="displayedColumns.length > 1" value="clearSelected">Clear Selection
        </mat-option>
        
        <ng-container *ngFor="let selectColumn of columns;">
          <mat-option *ngIf="!selectColumn.hidden" [value]="selectColumn.columnDef"
            [disabled]="selectColumn.selected">
            {{ selectColumn.header }}
          </mat-option>
        </ng-container>

      </mat-select>

    </mat-header-cell>

    <mat-cell *matCellDef="let row"> {{ column.cell(row) }} </mat-cell>

  </ng-container>

  <ng-container cdkColumnDef=''>
    <mat-header-cell *cdkHeaderCellDef> add </mat-header-cell>
    <mat-cell *cdkCellDef>test</mat-cell>
  </ng-container>

  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>

Here i am use material table for drage and drop column.

Step 4. Add code your typescript file.

import {
  CdkDragStart,
  CdkDropList,
  moveItemInArray
} from "@angular/cdk/drag-drop";
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator, MatSort, MatTableDataSource } from "@angular/material";
import "hammerjs";
import "rxjs/add/observable/of";
import * as _ from "lodash";

export interface PeriodicElement {
  name: string;
  node_status: string;
  rssi: string;
  wattage: string;
  brightness: string;
  current: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  {
    name: "node1",
    node_status: "On",
    rssi: "High",
    wattage: "123",
    brightness: "10",
    current: "0.12"
  },
  // ... additional elements
];

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  columns: any = [
    // ... existing columns
    {
      columnDef: "brightness",
      header: "Brightness",
      cell: element => `${element.brightness}`,
      selected: false,
      position: 0
    },
    {
      columnDef: "current",
      header: "Pole Current",
      cell: element => `${element.current}`,
      selected: false,
      position: 0
    }
  ];

  displayedColumns = this.columns
    .filter(obj => obj.selected)
    .map(obj => obj.columnDef);

  dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
  @ViewChild(MatSort) sort: MatSort;

  previousIndex: number;

  ngOnInit() {
    for (let i = 1; i <= 100; i++) {
      const hiddenElement = {
        columnDef: i + "-hidden",
        header: "",
        cell: element => null,
        selected: false,
        hidden: true
      };
      this.columns.push(hiddenElement);
    }
    this.setDisplayedColumns();
    this.dataSource.sort = this.sort;
  }

  setDisplayedColumns() {
    // console.log(this.displayedColumns);
  }

  dragStarted(event: CdkDragStart, index: number) {
    const prevIndex = _.indexOf(this.displayedColumns, event.source.data.name);
    console.log("dragStarted", event.source.data.name, prevIndex);
    this.previousIndex = prevIndex >= 0 ? prevIndex : 0;
  }

  dropListDropped(event: CdkDropList, index: number, columnDef) {
    if (event) {
      const dropIndex = _.indexOf(this.displayedColumns, columnDef);
      if (dropIndex >= 0) {
        console.log("dropListDropped", columnDef, dropIndex);
        moveItemInArray(this.displayedColumns, this.previousIndex, dropIndex);
        this.setDisplayedColumns();
      }
    }
  }

  switchColumn(event, prevColumn, position) {
    if (event.value == "clearSelected") {
      const newEmptyColumn = _.find(this.columns, function (emptyColumn) {
        return emptyColumn.hidden === true && emptyColumn.selected == false;
      });
      event.value = newEmptyColumn.columnDef;
    }

    console.log("switchColumn", prevColumn, event.value, position);

    if (prevColumn && event.value) {
      let prevColumnIndex = _.indexOf(this.displayedColumns, prevColumn);
      if (prevColumnIndex >= 0) {
        this.displayedColumns[prevColumnIndex] = event.value;
        let newValue = _.find(this.columns, ["columnDef", event.value]);
        newValue.selected = true;
        let prevColumnValue = _.find(this.columns, ["columnDef", prevColumn]);
        prevColumnValue.selected = false;
      }
    }
  }
}

Step 5. Here I have done it on Angular's table, you can do it on any table.