NgxDragDrop
Demo / StackBlitz Issue
Template
npm install ngx-drag-drop --save
Angular directives for declarative drag and drop using the HTML5 Drag-And-Drop API
- sortable lists by using placeholder element (vertical and horizontal)
- nestable
- dropzones optionally support external/native draggables (img, txt, file)
- conditional drag/drop
- typed drag/drop
- utilize EffectAllowed
- custom CSS classes
- touch support by using a polyfill
- AOT compatible
Port of angular-drag-drop-lists but without the lists :wink:
This has dropzones
though :+1:
The idea is that the directive does not handle lists internally so the dndDropzone
can be general purpose.
Usage
app.component.html
<div
[dndDraggable]="draggable.data"
[dndEffectAllowed]="draggable.effectAllowed"
[dndDisableIf]="draggable.disable"
(dndStart)="onDragStart($event)"
(dndCopied)="onDraggableCopied($event)"
(dndLinked)="onDraggableLinked($event)"
(dndMoved)="onDraggableMoved($event)"
(dndCanceled)="onDragCanceled($event)"
(dndEnd)="onDragEnd($event)">
<div
*ngIf="draggable.handle"
dndHandle>HANDLE
</div>
draggable ({{draggable.effectAllowed}}) <span [hidden]="!draggable.disable">DISABLED</span>
<div dndDragImageRef>DRAG_IMAGE</div>
</div>
<section
dndDropzone
(dndDragover)="onDragover($event)"
(dndDrop)="onDrop($event)">
dropzone
<div
style="border: 1px orangered solid; border-radius: 5px; padding: 15px;"
dndPlaceholderRef>
placeholder
</div>
</section>
app.component
import {Component} from '@angular/core';
import {DndDropEvent} from 'ngx-drag-drop';
@Component()
export class AppComponent {
draggable = {
data: "myDragData",
effectAllowed: "all",
disable: false,
handle: false
};
onDragStart(event: DragEvent) {
console.log("drag started", JSON.stringify(event, null, 2));
}
onDragEnd(event: DragEvent) {
console.log("drag ended", JSON.stringify(event, null, 2));
}
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("dragover", JSON.stringify(event, null, 2));
}
onDrop(event: DndDropEvent) {
console.log("dropped", JSON.stringify(event, null, 2));
}
}
app.module
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {DndModule} from 'ngx-drag-drop';
import {AppComponent} from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
DndModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
API
export type DropEffect = "move" | "copy" | "link" | "none";
export type EffectAllowed = DropEffect | "copyMove" | "copyLink" | "linkMove" | "all";
export type DndDragImageOffsetFunction = ( event:DragEvent, dragImage:Element ) => { x:number, y:number };
@Directive( {
selector: "[dndDraggable]"
} )
export declare class DndDraggableDirective {
dndDraggable: any;
dndEffectAllowed: EffectAllowed;
dndType?: string;
dndDisableIf: boolean;
dndDisableDragIf: boolean;
dndDraggingClass: string = "dndDragging";
dndDraggingSourceClass: string = "dndDraggingSource";
dndDraggableDisabledClass = "dndDraggableDisabled";
dndDragImageOffsetFunction:DndDragImageOffsetFunction = calculateDragImageOffset;
readonly dndStart: EventEmitter<DragEvent>;
readonly dndDrag: EventEmitter<DragEvent>;
readonly dndEnd: EventEmitter<DragEvent>;
readonly dndMoved: EventEmitter<DragEvent>;
readonly dndCopied: EventEmitter<DragEvent>;
readonly dndLinked: EventEmitter<DragEvent>;
readonly dndCanceled: EventEmitter<DragEvent>;
}
export interface DndDropEvent {
event: DragEvent;
dropEffect: DropEffect;
isExternal:boolean;
data?: any;
index?: number;
type?: any;
}
@Directive( {
selector: "[dndDropzone]"
} )
export declare class DndDropzoneDirective {
dndDropzone?: string[];
dndEffectAllowed: EffectAllowed;
dndDisableIf: boolean;
dndDisableDropIf: boolean;
dndAllowExternal: boolean;
dndHorizontal: boolean;
dndDragoverClass: string = "dndDragover";
dndDropzoneDisabledClass = "dndDropzoneDisabled";
readonly dndDragover: EventEmitter<DragEvent>;
readonly dndDrop: EventEmitter<DndDropEvent>;
}
Touch support
Install the mobile-drag-drop
module available on npm.
Add the following lines to your js code
import { polyfill } from 'mobile-drag-drop';
import { scrollBehaviourDragImageTranslateOverride } from "mobile-drag-drop/scroll-behaviour";
polyfill( {
dragImageTranslateOverride: scrollBehaviourDragImageTranslateOverride
} );
try {
window.addEventListener( "touchmove", function() { }, { passive: false } );
}
catch(e){}
For more info on the polyfill check it out on GitHub
https://github.com/timruffles/mobile-drag-drop
Known issues
Firefox
- Beware that Firefox does not support dragging on
<button>
elements.
Why?
HTML Drag-And-Drop API implementations are not behaving the same way across browsers.
The directives contained in this module enable declarative drag and drop that "just works" across browsers in a consistent way.
Credits go to the author and contributors of angular-drag-drop-lists.