20

I tried like this but onDrop method don't return image files when I drop to it...

onDragStart(event, data: any) {
  event.dataTransfer.setData('data', data);
}
onDrop(event, data: any) {
  let dataTransfer = event.dataTransfer.getData('data');
  event.preventDefault();
}
allowDrop(event) {
  event.preventDefault();
}
<div (drop)="onDrop($event, dropData)" (dragover)="allowDrop($event)"></div>
<div (dragstart)="onDragStart($event, dragData)"></div>

Any solution for this?

Jago
  • 2,751
  • 4
  • 27
  • 41
Vladimir
  • 1,751
  • 6
  • 30
  • 52

6 Answers6

32

The event onDrop event fires only when onDragOver has preventDefault() and stopPropagation() methods run on event.

HTML

<div
    (drop)="onDrop($event)"
    (dragover)="onDragOver($event)"
>
    Drop target
</div>

DropComponent.ts

export class DropComponent {
    onDrop(event) {
        event.preventDefault();
    }
    onDragOver(event) {
        event.stopPropagation();
        event.preventDefault();
    }
}

UPDATE

This is required because by default the browser prevents anything from happening while dropping onto the HTML element. See more at MDN - Defining a valid drop zone

maryl
  • 456
  • 5
  • 9
8

Here is the complete code for drag and drop in Angular 2/4/6 :

drag.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'drag-root',
  templateUrl: './drag.component.html',
  styleUrls: ['./drag.component.css']
})
export class AppComponent {

  allowDrop(ev) {
    ev.preventDefault();
  }

  drag(ev) {
    ev.dataTransfer.setData("text", ev.target.id);
  }

  drop(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData("text");
    ev.target.appendChild(document.getElementById(data));
  }
}

drag.component.html:

<h2>Drag and Drop</h2>
<div  id="div1" 
      (drop)="drop($event)" 
      (dragover)="allowDrop($event)">

      <img 
      src="https://images.pexels.com/photos/658687/pexels-photo-658687.jpeg?auto=compress&cs=tinysrgb&h=350" 
      draggable="true" 
      (dragstart)="drag($event)" 
      id="drag1"
      width="88" 
      height="31">
</div>

<div id="div2" 
  (drop)="drop($event)" 
  (dragover)="allowDrop($event)">
</div>

drag.component.css:

#div1, #div2 {
    float: left;
    width: 100px;
    height: 35px;
    margin: 10px;
    padding: 10px;
    border: 1px solid black;
}

Snapshots:

Drag 1

Drag 2

Shubham Verma
  • 8,783
  • 6
  • 58
  • 79
  • it works for me but for getting data from the div I used the below code and it gives the file Object, var data = event.dataTransfer.files[0]; – Meet Oct 06 '20 at 05:00
6

You could wrap the onDrop functionality into a reusable directive. Like this:

https://gist.github.com/darrenmothersele/7afda13d858a156ce571510dd78b7624

Apply this directive to an element:

<div (appDropZone)="onDrop($event)"></div>

The event is fired with a JavaScript array of the dropped Files. So, your onDrop implementation in the component looks something like this:

onDrop(files: FileList) {
  console.log({ files });
}
Darren
  • 111
  • 2
  • 3
1

As the others stated you need to call event.preventDefault() and event.stopPropagation() on the (dragover) event to make your container a valid dropzone.

I've written a highly customizable Angular component which implements the correct Drag'n'Drop behavior so I don't need to copy it over and over again which returns a list of the dropped files as an output event.
This can be found here.

After you imported the module you have access to the component:

<ngx-dropzone [multiple]="false" [maxFileSize]="2000"></ngx-dropzone>

You have some options to set and it comes with a decent default styling (screenshots can be found in the GitHub repo). If you want to, you can even take your own div container with your custom styles and hover effects and put it in the dropzone. Details on this can be found in the API description.

<ngx-dropzone [customContent]="customDropzone" (filesAdded)="onFilesDropped($event)">
<ng-template #customDropzone>
    <div class="custom-dropzone">
        This is my custom content
    </div>
</ng-template>

Kishan Oza
  • 1,707
  • 1
  • 16
  • 38
Peter Freeman
  • 81
  • 1
  • 3
  • I have spend 1 whole day for this but with your component its done in 3 mins.. I have one question can we show the uploaded images in that same dropbox ? – Kishan Oza Mar 07 '19 at 06:16
  • @KishanOza This is currently not implemented but I've got a lot of requests for it on GitHub as well. I'll try to add a preview for images in the next few weeks. I recommend you to 'watch' the repo so you'll get informed about updates. – Peter Freeman Mar 09 '19 at 10:22
  • yeah, I have manage to do by adding images in an array and display them separately. but it would be great if we can show the same images in that drop down box in self @PeterFreeman – Kishan Oza Mar 09 '19 at 20:45
  • It is now possible with the latest version. :) – Peter Freeman Mar 10 '19 at 11:44
  • Yeah that's cool.. but it's shows only one at a time image.. if I drop the new image it will override the previous one :( any solution? and there is no option to remove that image :( I am creating UI like playstore and appstore upload where user can add there screen shots – Kishan Oza Mar 11 '19 at 06:18
  • I'll look into it. – Peter Freeman Mar 11 '19 at 20:21
  • Updated it again. – Peter Freeman Mar 12 '19 at 20:04
1

A very simple directive that allows you to drop files wherever you place it.

I.E:

@Directive({
  selector: '[dropFileZone]'
})
export class DropFileZoneDirective {

  @Output() onDropFile = new EventEmitter<File>();
  constructor() { }

  @HostListener('dragover', ['$event'])
  allowDrop(event) {
    event.stopPropagation();
    event.preventDefault();
    event.dataTransfer.dropEffect = 'copy';
  }

  @HostListener('drop', ['$event'])
  async onDrop(event) {
    event.stopPropagation();
    event.preventDefault();
    let fileList = event.dataTransfer.files; // Array of all files
    const fileExists: boolean = fileList.length > 0;
    if (!fileExists) {
      return;
    }
    this.onDropFile.emit(fileList[0]);
  }
}

The way you use it on .html file :

<div dropFileZone (onDropFile)="handleDroppedFile($event)"> </div>
coderrr22
  • 327
  • 2
  • 10
0

You could try using Hostlistener decorator for the drag event, you can see it implemented for example here in ng2-file-upload

Vojtech
  • 2,756
  • 2
  • 19
  • 29