Skip to content

Instantly share code, notes, and snippets.

@darrenmothersele
Last active December 27, 2023 20:23
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save darrenmothersele/7afda13d858a156ce571510dd78b7624 to your computer and use it in GitHub Desktop.
Save darrenmothersele/7afda13d858a156ce571510dd78b7624 to your computer and use it in GitHub Desktop.
Angular Drag and Drop File Directive
import { Directive, EventEmitter, HostBinding, HostListener, Input, Output } from '@angular/core';
// Angular Drag and Drop File
//
// Add this directive to an element to turn it into a dropzone
// for drag and drop of files.
// Example:
//
// <div (appDropZone)="onDrop($event)"></div>
//
// Any files dropped onto the region are then
// returned as a Javascript array of file objects.
// Which in TypeScript is `Array<File>`
//
@Directive({
selector: '[appDropZone]'
})
export class DropZoneDirective {
// The directive emits a `fileDrop` event
// with the list of files dropped on the element
// as an JS array of `File` objects.
@Output('appDropZone') fileDrop = new EventEmitter<Array<File>>();
// Disable dropping on the body of the document.
// This prevents the browser from loading the dropped files
// using it's default behaviour if the user misses the drop zone.
// Set this input to false if you want the browser default behaviour.
@Input() preventBodyDrop = true;
// The `drop-zone-active` class is applied to the host
// element when a drag is currently over the target.
@HostBinding('class.drop-zone-active')
active = false;
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
event.preventDefault();
this.active = false;
const { dataTransfer } = event;
if (dataTransfer.items) {
const files = [];
for (let i = 0; i < dataTransfer.items.length; i++) {
// If dropped items aren't files, reject them
if (dataTransfer.items[i].kind === 'file') {
files.push(dataTransfer.items[i].getAsFile());
}
}
dataTransfer.items.clear();
this.fileDrop.emit(files);
} else {
const files = dataTransfer.files;
dataTransfer.clearData();
this.fileDrop.emit(Array.from(files));
}
}
@HostListener('dragover', ['$event'])
onDragOver(event: DragEvent) {
event.stopPropagation();
event.preventDefault();
this.active = true;
}
@HostListener('dragleave', ['$event'])
onDragLeave(event: DragEvent) {
this.active = false;
}
@HostListener('body:dragover', ['$event'])
onBodyDragOver(event: DragEvent) {
if (this.preventBodyDrop) {
event.preventDefault();
event.stopPropagation();
}
}
@HostListener('body:drop', ['$event'])
onBodyDrop(event: DragEvent) {
if (this.preventBodyDrop) {
event.preventDefault();
}
}
}
@nektobit
Copy link

Thanks so much for sharing this. Works great!

@nektobit
Copy link

Hi darrenmothersele, can you tell me how to use this snippet with input type = file? I tried this solution:

 <input
    type="file"
    [(ngModel)]="imgValue"
    [ngModelOptions]="{ standalone: true }"
   id="companyLogo"
 />

and

imgValue: any = '';
onDrop(dropFiles: FileList) {   
    this.imgValue = dropFiles[0];
}

but i have error:

InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

@darrenmothersele
Copy link
Author

You can't "set" the file on an input element in that way. What are you trying to do? Why do you need to set the input element file, rather than just handle the file drop in the onDrop handler?

@AdonousTech
Copy link

Works nicely! This is a great start to what I need to do.

@jerrythomas
Copy link

How do you get all files recursively within folders if a folder is dropped?

@darrenmothersele
Copy link
Author

I'm not sure. You might have to do something with the list of DataTransferItem from the DragEvent... docs:
https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList
https://developer.mozilla.org/en-US/docs/Web/API/DragEvent

There might be some useful info in this SO:
https://stackoverflow.com/questions/3590058/does-html5-allow-drag-drop-upload-of-folders-or-a-folder-tree

@jerrythomas
Copy link

Thank you. I will try them out.

@donreiman
Copy link

After using this directive to drag and drop a file onto the browser, the file is getting locked on windows. Even after completing the file upload and closing the browser tab, the file remains locked saying it is in use by chrome. I have to manually kill all chrome background processes to unlock the file. Same thing happens in edge. Anybody run into the same issue and find workaround?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment