How to get files from angular 2 onDrop event?
Asked Answered
C

6

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?

Crysta answered 23/3, 2017 at 21:22 Comment(2)
Why you prevent the default behavior of the events?Supply
Drag and drop not working if you not prevent...Crysta
B
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

Blather answered 27/5, 2017 at 18:7 Comment(2)
Would be great if you could explain why is this so.Kovno
To get the image file I used onDrop(event) { event.preventDefault(); img = event.dataTransfer.files; }Gingersnap
H
9

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

Hollow answered 25/6, 2018 at 14:47 Comment(1)
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];Cryptogam
S
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 });
}
Sympathizer answered 1/12, 2018 at 9:29 Comment(0)
C
2

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>
Caftan answered 19/1, 2022 at 9:51 Comment(1)
this stopped working for me in angular 17 looks like. the "dataTransfer" field doesn't exist under eventFriederike
S
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>

Submerge answered 5/1, 2019 at 20:30 Comment(7)
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 ?Shout
@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.Submerge
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 @PeterFreemanShout
It is now possible with the latest version. :)Submerge
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 shotsShout
I'll look into it.Submerge
Updated it again.Submerge
M
0

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

Mcclung answered 23/3, 2017 at 22:10 Comment(2)
How Hostlistener will help?Crysta
I tried to add @HostListener('drop', ['$event']) but it still return empty event...Crysta

© 2022 - 2024 — McMap. All rights reserved.