Drag and drop
Angular Bootstrap 5 Drag and drop plugin
Drag and Drop plugin built with Bootstrap 5. Examples of draggable list, cards, tables, grid, buttons. Available sort, copy, scroll, disable, delay, nested & other options.
Note: Read the API tab to find all available options and advanced customization
Draggable basic example
By adding mdbDraggable
directive you can make your custom element
draggable.
Drag me!
<div mdbDraggable class="draggable shadow-1-strong">
<p>Drag me!</p>
</div>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Custom container
If you want to have your draggable component in a container, just add
boundaryElement
directive with selector to your component.
Drag me!
<section
#draggableContainer
class="border p-4 mb-4"
style="height: 400px; overflow: hidden;"
>
<div mdbDraggable class="draggable shadow-1-strong" [boundaryElement]="draggableContainer">
<p>Drag me!</p>
</div>
</section>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Blocked axis
Thanks to [lockAxis]
input you can disable x or y axis.
Drag me!
Drag me!
<div mdbDraggable class="draggable shadow-1-strong" [lockAxis]="'x'">
<p>Drag me!</p>
</div>
<div mdbDraggable class="draggable shadow-1-strong" [lockAxis]="'y'">
<p>Drag me!</p>
</div>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Delay
You can set deley of starting dragging by adding
[delay]
input with miliseconds value.
Drag me after one second!
<div mdbDraggable [delay]="1000" class="draggable shadow-1-strong">
<p>Drag me after one second!</p>
</div>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Disabled
You can set your draggable element as disabled by adding
[disabled]
with true
value.
Disabled
<div mdbDraggable [disabled]="true" class="draggable shadow-1-strong">
<p>Disabled</p>
</div>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Scrolling option
When your draggable element is inside a scrollable container your component will scroll its while you will be near the edge.
Drag!
<div mdbDraggable [autoScroll]="true" class="draggable shadow-1-strong">
<p>Drag!</p>
</div>
.draggable {
display: flex;
width: 200px;
height: 200px;
justify-content: center;
align-items: center;
background-color: white;
position: relative;
}
.draggable-dragging {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
.draggable-drag-ico {
position: absolute;
top: 10px;
right: 10px;
font-size: 1.5rem;
color: grey;
}
Sortable basic example
By adding mdbSortableContainer
directive you
can add sortable functionality to the items in a container. Note, only elements with mdbDraggable
directive name will be
able to sort.
<div mdbSortableContainer class="sortable-list" (itemDrop)="onDrop($event)">
<div mdbDraggable *ngFor="let item of items" class="sortable-item">{{ item.name }}</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
{ name: 'Item 4' },
{ name: 'Item 5' }
];
onDrop(event: MdbDropEvent) {
moveItemsInContainer(this.items, event.previousIndex, event.newIndex);
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Horizontal example
Sortable list will create no matter how rotated it is.
<div mdbSortableContainer class="sortable-list d-flex" (itemDrop)="onDrop($event)">
<div mdbDraggable *ngFor="let item of items" class="sortable-item">{{ item.name }}</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
{ name: 'Item 4' },
{ name: 'Item 5' }
];
onDrop(event: MdbDropEvent) {
moveItemsInContainer(this.items, event.previousIndex, event.newIndex);
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
width: 100%;
border-bottom: none;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Grid example
Sortable list works with grid as well.
<div mdbSortableContainer class="sortable-list d-flex flex-wrap" (itemDrop)="onDrop($event)">
<div mdbDraggable *ngFor="let item of items" class="sortable-item">{{ item.name }}</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
items = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
{ name: 'Item 4' },
{ name: 'Item 5' },
{ name: 'Item 6' },
{ name: 'Item 7' },
{ name: 'Item 8' },
{ name: 'Item 9' },
{ name: 'Item 10' },
{ name: 'Item 11' },
{ name: 'Item 12' },
];
onDrop(event: MdbDropEvent) {
moveItemsInContainer(this.items, event.previousIndex, event.newIndex);
}
};
.sortable-list {
width: 500px;
max-width: 100%;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
justify-content: center;
align-items: center;
width: 125px;
height: 125px;
margin: 15px;
border: 1px solid #ccc;
text-align: center;
user-select: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Multiple tables
By using [containers]
input you can connect your list with other container.
To do
Done
<div class="d-flex">
<div #firstContainer="mdbSortableContainer" mdbSortableContainer [containers]="[secondContainer]" [data]="itemsFirst" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">To do</h4>
<div mdbDraggable [disabled]="item.disabled" *ngFor="let item of itemsFirst" class="sortable-item">{{ item.name }}</div>
</div>
<div #secondContainer="mdbSortableContainer" mdbSortableContainer [containers]="[firstContainer]" [data]="itemsSecond" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Done</h4>
<div mdbDraggable [disabled]="item.disabled" *ngFor="let item of itemsSecond" class="sortable-item">{{ item.name }}</div>
</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, moveItemToNewContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
itemsFirst = [
{ name: 'Item 1', disabled: false },
{ name: 'Item 2', disabled: false },
{ name: 'Item 3', disabled: false },
{ name: 'Item 4', disabled: false },
{ name: 'Item 5', disabled: false },
{ name: 'Disabled', disabled: true }
];
itemsSecond = [
{ name: 'Item 6', disabled: false },
{ name: 'Item 7', disabled: false },
{ name: 'Item 8', disabled: false },
{ name: 'Item 9', disabled: false },
{ name: 'Item 10', disabled: false }
];
onDrop(event: MdbDropEvent) {
if (event.previousContainer === event.newContainer) {
moveItemsInContainer(event.newContainer.data, event.previousIndex, event.newIndex);
} else {
moveItemToNewContainer(
event.previousContainer.data,
event.newContainer.data,
event.previousIndex,
event.newIndex
);
}
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Coping items
By adding [copy]
input with value true
you can copy your items to
connected table.
Elements
Copy
<div class="d-flex">
<div #firstContainer="mdbSortableContainer" mdbSortableContainer [containers]="[secondContainer]" [data]="itemsFirst" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Elements</h4>
<div mdbDraggable [copy]="item.copy" *ngFor="let item of itemsFirst" class="sortable-item">{{ item.name }}</div>
</div>
<div #secondContainer="mdbSortableContainer" mdbSortableContainer [containers]="[firstContainer]" [data]="itemsSecond" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Copy</h4>
<div mdbDraggable *ngFor="let item of itemsSecond" class="sortable-item">{{ item.name }}</div>
</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, copyItemToNewContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
itemsFirst = [
{ name: 'Item 1', copy: true },
{ name: 'Item 2', copy: true },
{ name: 'Item 3', copy: true },
{ name: 'Item 4', copy: true },
{ name: 'Item 5', copy: true },
];
itemsSecond = [
{ name: 'Item 6' },
{ name: 'Item 7' },
{ name: 'Item 8' },
{ name: 'Item 9' },
{ name: 'Item 10' },
];
onDrop(event: MdbDropEvent) {
if (event.previousContainer === event.newContainer) {
moveItemsInContainer(event.newContainer.data, event.previousIndex, event.newIndex);
} else {
copyItemToNewContainer(
event.previousContainer.data,
event.newContainer.data,
event.previousIndex,
event.newIndex
);
}
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Conditions
You can set your own conditions about permission to sending or coping items to connected table
by adding your custom function with true
/ false
return to
[enterPredicate]
input.
Numbers
Only odd numbers
<div class="d-flex">
<div #firstContainer="mdbSortableContainer" mdbSortableContainer [containers]="[secondContainer]" [data]="itemsFirst" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Numbers</h4>
<div mdbDraggable [data]="item.data" *ngFor="let item of itemsFirst" class="sortable-item">{{ item.name }}</div>
</div>
<div #secondContainer="mdbSortableContainer" mdbSortableContainer [containers]="[firstContainer]" [data]="itemsSecond" [enterPredicate]="enterPredicate" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Only odd numbers</h4>
<div mdbDraggable [data]="item.data" *ngFor="let item of itemsSecond" class="sortable-item">{{ item.name }}</div>
</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, moveItemToNewContainer, MdbDropEvent, MdbDraggableDirective } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
itemsFirst = [
{ name: 'Item 1', data: '1' },
{ name: 'Item 2', data: '2' },
{ name: 'Item 3', data: '3' },
{ name: 'Item 4', data: '4' },
{ name: 'Item 5', data: '5' },
];
itemsSecond = [
{ name: 'Item 7', data: '7' },
];
onDrop(event: MdbDropEvent) {
if (event.previousContainer === event.newContainer) {
moveItemsInContainer(event.newContainer.data, event.previousIndex, event.newIndex);
} else {
moveItemToNewContainer(
event.previousContainer.data,
event.newContainer.data,
event.previousIndex,
event.newIndex
);
}
}
enterPredicate(item: MdbDraggableDirective) {
return item.data % 2 !== 0;
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Disabled sorting
By setting sorting
input to false
you can disable sorting
in table.
Sorting available
Sorting not available
<div class="d-flex">
<div #firstContainer="mdbSortableContainer" mdbSortableContainer [containers]="[secondContainer]" [data]="itemsFirst" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Sorting available</h4>
<div mdbDraggable *ngFor="let item of itemsFirst" class="sortable-item">{{ item.name }}</div>
</div>
<div #secondContainer="mdbSortableContainer" mdbSortableContainer [containers]="[firstContainer]" [data]="itemsSecond" [sortingDisabled]="true" class="sortable-list" (itemDrop)="onDrop($event)">
<h4 class="text-center pt-2">Sorting disabled</h4>
<div mdbDraggable *ngFor="let item of itemsSecond" class="sortable-item">{{ item.name }}</div>
</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, moveItemToNewContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
itemsFirst = [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
{ name: 'Item 4' },
{ name: 'Item 5' },
];
itemsSecond = [
{ name: 'Item 6' },
{ name: 'Item 7' },
];
onDrop(event: MdbDropEvent) {
if (event.previousContainer === event.newContainer) {
moveItemsInContainer(event.newContainer.data, event.previousIndex, event.newIndex);
} else {
moveItemToNewContainer(
event.previousContainer.data,
event.newContainer.data,
event.previousIndex,
event.newIndex
);
}
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Nested
To do
Done
<div
id="sortable-multi-1-1"
class="sortable-list d-flex align-items-start"
mdbSortableContainer
[data]="nestedContainers"
(itemDrop)="onDrop($event)"
>
<div
*ngFor="let container of nestedContainers"
class="sortable-list sortable-item-nested"
mdbSortableContainer
[id]="container.id"
[data]="container.data"
[containers]="getConnectedContainers(container.id)"
mdbDraggable
(itemDrop)="onDrop($event)"
>
<h4 class="text-center pt-2 drag-handler">{{ container.name }}</h4>
<div mdbDraggable *ngFor="let item of container.data" class="sortable-item">{{ item.name }}</div>
</div>
</div>
import { Component } from '@angular/core';
import { moveItemsInContainer, moveItemToNewContainer, MdbDropEvent } from 'mdb-angular-drag-and-drop';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
nestedContainers = [
{
id: 'nested-sortable-1',
name: 'To do',
data: [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' },
{ name: 'Item 4' },
{ name: 'Item 5' },
],
},
{
id: 'nested-sortable-2',
name: 'Done',
data: [{ name: 'Item 6' }, { name: 'Item 7' }, { name: 'Item 8' }, { name: 'Item 9' }],
},
];
getConnectedContainers(id: string) {
return this.nestedContainers.filter((container) => container.id !== id).map((container) => container.id);
}
onDrop(event: MdbDropEvent) {
if (event.previousContainer === event.newContainer) {
moveItemsInContainer(event.newContainer.data, event.previousIndex, event.newIndex);
} else {
moveItemToNewContainer(
event.previousContainer.data,
event.newContainer.data,
event.previousIndex,
event.newIndex
);
}
}
};
.sortable-list {
width: 500px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: block;
background: #fff;
border-radius: 4px;
}
.sortable-item {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
background: #fff;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
user-select: none;
}
.sortable-item:last-child {
border: none;
}
.draggable-helper {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.21) !important;
}
Drag and drop - API
Import
import { MdbDragAndDropModule } from 'mdb-angular-drag-and-drop';
…
@NgModule ({
...
imports: [MdbDragAndDropModule],
...
})
Inputs
mdbDraggable
Name | Type | Default | Description |
---|---|---|---|
boundaryElement
|
HTMLElement | String | body |
Defines boundary container for dragged element |
copy
|
Boolean | false |
Defines if dragged element should be moved or copied to new container |
data
|
any | - |
Defines draggable element data |
delay
|
Number | 0 |
Defines how long will deley exist before element starts to drag |
disabled
|
Boolean | false |
Defines whether element is able to drag or not |
handle
|
HTMLElement | String | - |
Defines drag handler of the element. Note, handler has to be inside of the dragging element |
lockAxis
|
'x' | 'y' | null | null |
Defines whether 'x' or 'y' axis is blocked |
mdbSortableContainer
Name | Type | Default | Description |
---|---|---|---|
containers
|
MdbSortableContainer | String | [] |
Defines list of connected containers |
enterPredicate
|
Function | () => true |
Defines function which check access between tables |
sortingDisabled
|
Boolean | false |
Defines whether list is able to sort or not |
Outputs
mdbSortableContainer
Name | Description |
---|---|
(itemDrop)
|
Emitted on element drop in container |