Skip to content

Instantly share code, notes, and snippets.

@dirkluijk dirkluijk/drag-drop-part 1.md Secret

Created Mar 18, 2018
Embed
What would you like to do?
Drag/drop in Angular - part 1

Drag/drop in Angular: part 1 - the draggable directive

The first part of this series starts with the foundation: the draggable directive. This directive has only one responsibility: tracking the dragStart, dragMove and dragEnd events.

Background

To have the ultimate freedom, I recommend to stay away from the HTML5 Drag and Drop API. Of course, HTML5 drag/drop can be extremely useful, especially for file uploads with dropzones. But it also has limitations. For example, it can be hard to disable the so-called "ghost element", when you want to use your own helper element. Or what about the dragover event, which is fired every few hundred milliseconds?

For me, HTML5 pointer events were good enough. For some browsers you will need the polyfill, but you can easily install it and activate it in your polyfills.ts of your Angular app.

Summary

This is how I want to use the draggable directive:

<div class="box" appDraggable>
  My box!
</div>

It should also have 3 output event emitters for the dragStart, dragMove and dragEnd events:

<div class="box" 
     appDraggable
     (dragStart)="onDragStart($event)"
     (dragMove)="onDragMove($event)"
     (dragEnd)="onDragEnd($event)">
  My box!
</div>

A simple version of the directive looks like this:

@Directive({
  selector: '[appDraggable]'
})
export class DraggableDirective {
  @Output() dragStart = new EventEmitter<PointerEvent>();
  @Output() dragMove = new EventEmitter<PointerEvent>();
  @Output() dragEnd = new EventEmitter<PointerEvent>();

  private dragging = false;

  @HostListener('pointerdown', ['$event'])
  onPointerDown(event: PointerEvent): void {
    this.dragging = true;
    this.dragStart.emit(event);
  }

  @HostListener('document:pointermove', ['$event'])
  onPointerMove(event: PointerEvent): void {
    if (!this.dragging) {
      return;
    }

    this.dragMove.emit(event);
  }

  @HostListener('document:pointerup', ['$event'])
  onPointerUp(event: PointerEvent): void {
    if (!this.dragging) {
      return;
    }

    this.dragging = false;
    this.dragEnd.emit(event);
  }
}

I also demonstrated a more reactive approach using RxJS. Please watch my YouTube video to see both implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.