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!
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.
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.
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.
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.
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.