1. Initialize draggable element


We initialize this template in a DragDropComponent:


  <div #draggable class="draggable"></div>
  .draggable {
      width: 200px;
      height: 200px;
      background-color: #ccc
  }

2. Initialize events


We init observables from events that we will need later:


import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Observable, fromEvent } from 'rxjs';

@Component({
  selector: 'app-drag-drop',
  templateUrl: './drag-drop.component.html',
  styleUrls: ['./drag-drop.component.scss']
})
export class DragDropComponent implements OnInit {

  @ViewChild('draggable', { static: true }) draggable: ElementRef; // get html element

  private start$: Observable<MouseEvent>;
  private move$: Observable<MouseEvent>;
  private stop$: Observable<MouseEvent>;

  constructor() {}

  ngOnInit(): void {
    this.start$ = fromEvent(this.draggable.nativeElement, 'mousedown');
    this.move$ = fromEvent(document, 'mousemove') as Observable<MouseEvent>;
    this.stop$ = fromEvent(document, 'mouseup') as Observable<MouseEvent>;
  }
}


3. Create drag and drop logic


  import { fromEvent, Observable, switchMap, takeUntil } from 'rxjs';
  (...)

  private drag$: Observable<MouseEvent>;

  ngOnInit(): void {
    (...)

    this.drag$ = this.start$.pipe( // init with the mousedown on draggable element
      switchMap(() => this.move$.pipe( // redirect to mousemove
        takeUntil(this.stop$) // stop when mouseup
      ))
    );
  }


4. Subscribe and make moves visible


  ngOnInit(): void {
    (...)

    this.drag$.subscribe(event => {
      const { layerX: x, layerY: y } = event as any; // type any in order to use non-standard layerX and layerY propertiesthis.draggable.nativeElement.style.transform = `translateX(${x}px) translateY(${y}px)`;
    });
  }


And here you go! 🥳


Note : some angular libraries for drag & drop use this technique to handle events, for example angular-draggable-droppable.