11

I would like to implement a drag and drop using Angular 2. I have some items:

<div class="item"></div>

which I would like to be able to drag and drop in a container:

<div class="container"></div>

I can not find any good source of information for doing this in Angular 2. I found this file: https://github.com/AngularClass/angular2-examples/blob/master/rx-draggable/directives/draggable.ts which I tried but I could not get it to work, I am also not entirely sure on how it should work.

How do I implement it?

3

7 Answers 7

22

try this:

function onDragStart(event, data) {
  event.dataTransfer.setData('data', data);
}
function onDrop(event, data) {
  let dataTransfer = event.dataTransfer.getData('data');
  event.preventDefault();
}
function allowDrop(event) {
  event.preventDefault();
}
<div (drop)="onDrop($event, dropData)" (dragover)="allowDrop($event)"></div>
<div draggable="true" (dragstart)="onDragStart($event, dragData)"></div>
Sign up to request clarification or add additional context in comments.

2 Comments

What is dropData for/What is it?
Not that easy as it looks. When there are are multiple drop zones & multiple cards to be dragged/dropped, due to angular change detection, it is really slow when we use html5 drag/drop feature.
8

Try this:

systemjs.config:

var map =       {
    'app': './wwwroot/ngApp',
    'rxjs': './wwwroot/js/libs/rxjs',
    '@angular': './wwwroot/js/libs/@angular',
    'dragula': './wwwroot/js/libs/dragula/dist/dragula.js',
    'ng2-dragula': './wwwroot/js/libs/ng2-dragula'
  };

var packages = {
    'app': { main: 'main.js', defaultExtension: 'js' },
    'rxjs': { defaultExtension: 'js' },
    'dragula': { defaultExtension: 'js' },
    'ng2-dragula': {defaultExtension: 'js' }
  };

var config = {
    map: map,
    packages: packages  
  }`

Then import

import {Dragula, DragulaService} from 'ng2-dragula/ng2-dragula';

And in @Component

directives: [Dragula], viewProviders: [DragulaService]

3 Comments

That's a very nice implementation of drag and drop. I really like your business logic there. Not enough jquery though
unable to find this file 'systemjs.config', where it is located?
@pjay probably on Angular CLI, just run 'npm install --save ng2-dragula' instead of the first step.
6

I would recommend using Ng2-Dragula.

it is the angular2 dependency which provides drag n drop functionality to your application easily.

All you need to do is to install this dependency through npm.

npm install ng2-dragula dragula --save

add includes inside index.html and configure system as

<script src="/node_modules/ng2-dragula/bundles/ng2-dragula.js"></script>
<link href="/node_modules/ng2-dragula/src/public/css/dragula.min.css" rel='stylesheet' type='text/css'>
<script>
    System.config({        
    paths:{
        'dragula'         : '../node_modules/dragula/dist/dragula.min.js'
    },
    packages: {            
      app: {
        format: 'register',
        defaultExtension: 'js'
      }
    }
  });

 System.import('app/main')
       .then(null, console.error.bind(console));
</script>

import it inside the component where you want to use drag n drop and you are good to go.

@Component({
  selector: 'sample',
  directives: [Dragula],
  viewProviders: [DragulaService],
  template:`
  <div>
    <div class='wrapper'>
      <div class='container' [dragula]='"first-bag"'>
        <div>You can move these elements between these two containers</div>
        <div>Moving them anywhere else isn't quite possible</div>
        <div>There's also the possibility of moving elements around in the same container, changing their position</div>
      </div>
      <div class='container' [dragula]='"first-bag"'>
        <div>This is the default use case. You only need to specify the containers you want to use</div>
        <div>More interactive use cases lie ahead</div>
        <div>Make sure to check out the <a href='https://github.com/bevacqua/dragula#readme'>documentation on GitHub!</a></div>
      </div>
    </div>
  </div>
  `
})
class Sample {}

Comments

4

I also started out with the same example for my draggables - and did get it to work. The example is from an early version of angular2, so some changes are necessary. Check out this answer. It has some of those changes in it. Best of luck!

My own slightly more general purpose version goes like this:

import {Directive, EventEmitter, HostListener, Output} from 'angular2/core';
import {Observable} from 'rxjs/Observable';

@Directive({
  selector: '[draggable]'
})
export class DraggableDirective {

  @Output() mousedrag: Observable<{x: number, y: number}>;
  @Output() dragend = new EventEmitter<void>();
  mousedown = new EventEmitter<MouseEvent>();
  mousemove = new EventEmitter<MouseEvent>();
  dragActive = false;

  @HostListener('document:mouseup', ['$event'])
  onMouseup(event) {
    if(this.dragActive) {
      this.dragend.emit(null);
      this.dragActive = false;
    }
  }

  @HostListener('mousedown', ['$event'])
  onMousedown(event: MouseEvent) {
    this.mousedown.emit(event);
  }

  @HostListener('document:mousemove', ['$event'])
  onMousemove(event: MouseEvent) {
    if(this.dragActive) {
      this.mousemove.emit(event);
      return false;
    }
  }

  constructor() {
    this.mousedrag = this.mousedown.map((event) => {
      this.dragActive = true;
      return { x: event.clientX, y: event.clientY };
    }).flatMap(mouseDownPos => this.mousemove.map(pos => {
      return { x: pos.clientX - mouseDownPos.x, y: pos.clientY - mouseDownPos.y };
    }).takeUntil(this.dragend));
  }
}

Take that with a pinch of salt as I am currently chasing a memory leak which seems to be related to this directive. I will update if I find an issue.

Comments

3

I have done it using jquery draggable - integrated in Angular

import {Component, ElementRef, OnInit} from '@angular/core';'

declare var jQuery:any;

@Component({
    selector: 'jquery-integration',
    templateUrl: './components/jquery-integration/jquery-integration.html'
})
export class JqueryIntegration implements OnInit {
    elementRef: ElementRef;
    constructor(elementRef: ElementRef) {
        this.elementRef = elementRef;
    }
    ngOnInit() {
        jQuery(this.elementRef.nativeElement).draggable({containment:'#draggable-parent'});
    }
}

More info here: http://www.syntaxsuccess.com/viewarticle/using-jquery-with-angular-2.0

Live demo: http://www.syntaxsuccess.com/angular-2-samples/#/demo/jquery

1 Comment

I would rather do it in Angular2 without the use of jQuery. As it is said in the article you linked, using jQuery is not the Angular way of doing things. But I will have this solution in mind if I do not find anything better.
0

Russian's answer works well but change detection makes it slow. You can fix this by using a custom directive.

Credit for this comes from here https://netbasal.com/angular-2-escape-from-change-detection-317b3b44906b

import {Directive, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output} from '@angular/core';

@Directive({
  selector: '[appIgnoreEvent]'
})
export class IgnoreEventDirective implements OnInit, OnDestroy {
  @Input() event = 'dragover';
  @Output() emitter = new EventEmitter();
  private _handler: Function;
  constructor(private _ngZone: NgZone, private el: ElementRef) {}

  ngOnInit() {
    this._ngZone.runOutsideAngular(() => {
      const nativeElement = this.el.nativeElement;
      this._handler = $event => {
        this.emitter.emit($event);
      };

      nativeElement.addEventListener(this.event, this._handler, false);
    });
  }

  ngOnDestroy() {
    this.el.nativeElement.removeEventListener(this.event, this._handler);
  }
}

Then pass the dragOver event to your emitter instead.

<div (drop)="onDrop($event, dropData)" appIgnoreEvent (emitter)="allowDrop($event)"></div>

Not enough reputation for me to add this as a comment

Comments

0

i made this component for one of my projects hope this will help.

import { Component, OnInit, ViewChild, ElementRef, HostListener } from '@angular/core';

@Component({
    selector: 'app-video-call-container',
    templateUrl: './video-call-container.component.html',
    styleUrls: ['./video-call-container.component.css']
})
export class VideoCallContainerComponent implements OnInit {

    constructor() { }

    mouseCursorX = 0;
    mouseCursorY = 0;
    dragActive = false;



    @ViewChild('container') container: ElementRef;

    @HostListener('window:mouseup', ['$event'])
    mouseUp(event) {
        if (this.dragActive == true) {
            this.dragActive = false;
        }
    }

    @HostListener('window:mousemove', ['$event'])
    mouseMove(event) {
        if (this.dragActive) {
            var left = this.mouseCursorX - event.clientX;
            var top = this.mouseCursorY - event.clientY;
            var offsets = this.getElementOffsets(this.container.nativeElement);
            var posLeft = (offsets.left - left);
            var posTop = (offsets.top - top);
            if (posLeft > 0 && posLeft <= window.innerWidth - this.container.nativeElement.offsetWidth && posTop > 0 && posTop <= window.innerHeight - this.container.nativeElement.offsetHeight) {
                this.container.nativeElement.style.left = posLeft + "px";
                this.container.nativeElement.style.top = posTop + "px";
            }
            this.mouseCursorX = event.clientX;
            this.mouseCursorY = event.clientY;
        }
    }

    drag(event) {
        this.dragActive = true;
        this.mouseCursorX = event.clientX;
        this.mouseCursorY = event.clientY;
    }


    getElementOffsets(elem) {
        return {
            top: this.getOffsetTop(elem),
            left: this.getOffsetLeft(elem)
        }
    }

    getOffsetTop(elem) {
        var offsetTop = 0;
        do {
            if (!isNaN(elem.offsetTop)) {
                offsetTop += elem.offsetTop;
            }
        } while (elem = elem.offsetParent);
        return offsetTop;
    }

    getOffsetLeft(elem) {
        var offsetLeft = 0;
        do {
            if (!isNaN(elem.offsetLeft)) {
                offsetLeft += elem.offsetLeft;
            }
        } while (elem = elem.offsetParent);
        return offsetLeft;
    }


    ngOnInit(): void {
    }

}
<div class="container-box" #container>
    <div class="container-header" (mousedown)="drag($event)">
        <label>Vikas Kandari</label>
        <span>Ringing...</span>
        <button><i class="fa fa-close"></i></button>
    </div>
    <div class="container-body">
        <div class="video-call-caller">
            <video></video>
        </div>
        <div class="video-call-receiver">
            <video></video>
        </div>
    </div>
</div>
.container-box {
    position: fixed;
    background-color: #fefefe;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0,0,0,.15), 0 2px 3px rgba(0,0,0,.2);
    z-index: 9999999999999;
    right: 15px;
    bottom: 50px;
    width: 300px;
    height: 400px;
}

.container-header {
    width: 100%;
    float: left;
    padding: 10px;
    border-bottom: 1px solid #ddd;
    cursor: move;
}

.container-header>label {
    display: block;
    width: 100%;
    float: left;
    margin: 0px;
    cursor: move;
}

.container-header>button {
    position: absolute;
    right: 10px;
    top: 10px;
    border-radius: 100%;
    height: 30px;
    width: 30px;
    border: none;
    font-size: 20px;
    background: transparent;
    cursor: pointer;
}

.container-header>span {
    display: block;
    float: left;
    width: 100%;
    cursor: move;
}

.container-header:hover>button {
    background: #e6ecf0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.