Telerik blogs

Plain JavaScript or a library can allow you to add drag-and-drop capabilities inside your Angular app with ease.

Incorporating drag and drop features into our Angular web apps is easy with plain JavaScript or a library. Let’s see how in this post!

Add Drag and Drop with Plain JavaScript

We can add drag and drop to our Angular apps easily with plain JavaScript. To do this, we listen to native drag and drop events.

For instance, we write:

app.component.ts

import { Component } from "@angular/core";

interface Draggable {
  id: number;
  name: string;
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  dragList: Draggable[] = [
    { id: 1, name: "foo" },
    { id: 2, name: "bar" },
    { id: 3, name: "baz" },
    { id: 4, name: "qux" },
    { id: 5, name: "abc" },
  ];
  dropList: Draggable[] = [];

  onDragStart(event: DragEvent, draggedObject: Draggable) {
    const index = this.dragList.findIndex((l) => l.id === draggedObject.id);
    event?.dataTransfer?.setData("text", index.toString());
  }

  onDragOver(event: DragEvent) {
    event.preventDefault();
  }

  onDrop(event: DragEvent) {
    const indexOfItemBeingDragged = Number(
      event?.dataTransfer?.getData("text")
    );
    if (typeof indexOfItemBeingDragged !== "number") {
      return;
    }
    const draggedObject = this.dragList[indexOfItemBeingDragged];
    this.dragList.splice(indexOfItemBeingDragged, 1);
    this.dropList.push(draggedObject);
    event?.dataTransfer?.clearData?.();
  }
}

We make arrays with the dragList and dropList for the drag-and-drop contents. Then we listen for the dragstart, dragover and drop events with the methods listed.

onDragStart will be called when we start dragging an item with the draggable attribute set to true.

We call event.dataTransfer.setData to add data for the item we’re dragging. Then in onDragOver we call event.preventDefault so we can drop items.

And then in onDrop, we get the value we put in setData's second argument with getData and the same format string we passed into setData.

Since we passed in the index of the item in dragList we’re dragging into setData, we can get that from getData and use that to remove the item from dragList with splice and put the item in dropList.

And then we call clearData to clear the data transfer data we put in with setData.

In app.component.html, we add:

<h1>Drag List</h1>
<div>
  <div
    *ngFor="let d of dragList"
    draggable="true"
    (dragstart)="onDragStart($event, d)"
  >
    {{ d.name }}
  </div>
</div>

<h1>Drop List</h1>
<div
  (dragover)="onDragOver($event)"
  (drop)="onDrop($event)"
  style="
    border: 1px orangered solid;
    border-radius: 5px;
    padding: 15px;
    min-height: 300px;
  "
>
  <div *ngFor="let d of dropList">{{ d.name }}</div>
</div>

We make items draggable by setting the draggable attribute to true. We make all items in dragList draggable.

To get the data of the object we’re dragging, we pass in the d object to onDragStart so we can call setData with the index of the item we’re dragging.

We then render the dropList items which will be shown after we drop some things from the Drag List onto the bottom rectangle.

Then we make a drop zone by listening to the dragover and drop events.

Add Drag and Drop Using the ngx-drag-drop Library

Also, we can add drag and drop features easily into our Angular apps using the ngx-drag-drop library.

To install it, we run:

npm i ngx-drag-drop

Then we can add drag-and-drop lists easily with the directives and events provided.

First, we add the DndModule into our app’s module by writing:

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { DndModule } from "ngx-drag-drop";

import { AppComponent } from "./app.component";

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, DndModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

This will let us use ngx-drag-drop in the components in AppModule.

Then in app.component.ts, we write:

import { Component } from "@angular/core";
import { DndDropEvent, EffectAllowed } from "ngx-drag-drop";

interface Draggable {
  id: number;
  name: string;
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  dragList: Draggable[] = [
    { id: 1, name: "foo" },
    { id: 2, name: "bar" },
    { id: 3, name: "baz" },
    { id: 4, name: "qux" },
    { id: 5, name: "abc" },
  ];
  dropList: Draggable[] = [];
  draggable = {
    data: "myDragData",
    effectAllowed: "all" as EffectAllowed,
    disable: false,
    handle: false,
  };
  indexOfItemBeingDragged: number | undefined = undefined;

  onDragStart(event: DragEvent, draggedObject: Draggable) {
    const index = this.dragList.findIndex((l) => l.id === draggedObject.id);
    this.indexOfItemBeingDragged = index;
  }

  onDragEnd(event: DragEvent, draggedObject: Draggable) {
    const index = this.dragList.findIndex((l) => l.id === draggedObject.id);
    this.indexOfItemBeingDragged = index;
  }

  onDraggableCopied(event: DragEvent) {
    console.log("draggable copied", JSON.stringify(event, null, 2));
  }

  onDraggableLinked(event: DragEvent) {
    console.log("draggable linked", JSON.stringify(event, null, 2));
  }

  onDraggableMoved(event: DragEvent) {
    console.log("draggable moved", JSON.stringify(event, null, 2));
  }

  onDragCanceled(event: DragEvent) {
    console.log("drag cancelled", JSON.stringify(event, null, 2));
  }

  onDragover(event: DragEvent) {
    console.log("drag over", JSON.stringify(event, null, 2));
  }

  onDrop(event: DndDropEvent) {
    if (typeof this.indexOfItemBeingDragged !== "number") {
      return;
    }
    const draggedObject = this.dragList[this.indexOfItemBeingDragged];
    this.dragList.splice(this.indexOfItemBeingDragged, 1);
    this.dropList.push(draggedObject);
    this.indexOfItemBeingDragged = undefined;
  }
}

We added the dragList and dropList arrays with arrays Draggable objects.

We want to put the objects in dragList into dropList by providing a user interface for users to move the items from dragList to dropList.

The methods in AppComponent are event handler functions for various drag events. We add the draggable object with some options we provided to ngx-drag-drop.

Since we want to move the dragged item from dragList to dropList when dragging is finished, we add the logic to do that in the onDragEnd method.

We get the item that is dragged by the id value with findIndex. Then we remove that from dragList with splice and the index returned by findIndex. And then we add that object into dropList with push.

Then in the template, we add the drag-and-drop list HTML. To do this, we write:

app.component.html

<h1>Drag List</h1>
<div
  *ngFor="let d of dragList"
  [dndDraggable]="draggable.data"
  [dndEffectAllowed]="draggable.effectAllowed"
  [dndDisableIf]="draggable.disable"
  (dndStart)="onDragStart($event, d)"
  (dndCopied)="onDraggableCopied($event)"
  (dndLinked)="onDraggableLinked($event)"
  (dndMoved)="onDraggableMoved($event)"
  (dndCanceled)="onDragCanceled($event)"
  (dndEnd)="onDragEnd($event, d)"
>
  {{ d.name }}
  <div *ngIf="draggable.handle" dndHandle>HANDLE</div>

  <span [hidden]="!draggable.disable">DISABLED</span>
</div>

<h1>Drop List</h1>
<section
  dndDropzone
  (dndDragover)="onDragover($event)"
  (dndDrop)="onDrop($event)"
  style="border: 1px orangered solid; border-radius: 5px; padding: 15px"
>
  <div *ngFor="let d of dropList">{{ d.name }}</div>
  <div dndPlaceholderRef>Drop here</div>
</section>

We render the objects in dragList into divs with ngFor. And we make them draggable by adding the directives and listening to events provided by ngx-drag-drop.

We listen for the ending of the dragging action by listening to the dndEnd event.

And we pass in the d object that we want to move to the dropList by passing it into the method.

It also emits the dndStart event when we start dragging.

The dndCopied event is emitted when an item is dragged with the copy effect.

The dndLinked event is emitted when an item is dragged with the link effect.

The dndCanceled event is emitted when dragging is canceled

And the dndDrag event is emitted when an item is being dragged.

We set the dndEffectAllowed attribute to set the drag effect allowed. Effects allowed includes 'move', 'copy', 'link', 'none', 'copyMove', 'copyLink', 'linkMove' and 'all'.

In the drop zone section element, we add the dndDropzone directive.

We set the indexOfItemBeingDragged when we start dragging by setting that in the onDragStart method. This way, we know what we are dragging when we start dragging.

Likewise, we do the same thing on onDragEnd so we know the index of the item being dragged.

Finally, in the onDrop method, we get the draggedObject by the indexOfItemBeingDragged value from the dragList. And then we call dragList.splice to remove the item by the indexOfItemBeingDragged from dragList.

Next, we add the item to the dropList with push. And we set indexOfItemBeingDragged back to undefined so we know nothing is being dragged now.

We add the dndDropzone to the drop zone element to make it a drop zone element. Then we can listen to the events listed there.

And we add the dndPlaceholderRef directive to make the div show when we are dragging items over it. As a result, we should see that we can drag the words from the top list to the drop zone and the words stay there.

Kendo UI for Angular Drag and Drop Functionality

If you’re using the Progress Kendo UI for Angular component library to speed up your development, you can add pre-built drag and drop functionality to define any HTML element or Angular component as either drag or drop target.

Further customize the interaction between targets using a wide range of properties and events. See the provided demo:

Kendo UI for Angular comes with a free 30-day trial you can check out today.

Conclusion

Angular is a framework that provides us with an organized way to build frontend web apps. Sometimes, we want to incorporate drag and drop features into our web apps. We can do this easily with plain JavaScript or a library.

The ngx-drag-drop library lets us customize drag-and-drop options more easily.

Plain JavaScript allows us to add drag and drop without adding a new library. It is also simple to add drag and drop with native API using plain JavaScript.

And a commercial library like Kendo UI for Angular equips you to add drag and drop to any number of other pre-built UI components.


About the Author

John Au-Yeung

John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.

Related Posts

Comments

Comments are disabled in preview mode.