admin管理员组

文章数量:1307166

I'm trying to implement a drag and drop file upload in angular 8, heavily inspired by this article, and this one.

I cant get any of the drag events to fire. I've checked that the DragDropDirective is imported by using mouseenter methods in the same directive, which launch correctly, i also see that style that i added is correctly applied.

I've been banging my head against this all day, what am i missing?

Edit: This works with files from a Files explorer, but not with files from the desktop. Using Ubuntu 19.

// ponent.html
<section appDragDrop class="panel" id="dropzone-wrapper" (onFileDropped)="uploadFile($event)">
    <input type="file" #fileInput (change)="uploadFile($event.target.files)" accept=".json" hidden>
    <label for="dropzone">
        <span>
            drag and drop here.
        </span>
    </label>
</section>

import { Directive, Output, Input, EventEmitter, HostBinding, HostListener, ElementRef } from '@angular/core';

@Directive({
  selector: '[appDragDrop]'
})
export class DragDropDirective {
  el: ElementRef
  constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }

  // Working fine
  @HostListener('mouseenter') onMouseEnter() {
    console.log('mouse entering')
  }
  
  // Working fine
  @HostListener('mouseleave') onMouseLeave() {
    console.log('mouse leaving')
  }

  // --- Not working ---
  @HostListener('dragover', ['$event']) onDragOver(evt) {
    console.log('A')
  }

  // --- Not working ---
  @HostListener('dragleave', ['$event']) public onDragLeave(evt) {
    console.log('B')
  }

  // --- Not working ---
  @HostListener('drop', ['$event']) public ondrop(evt) {
    console.log('C')
  }
}

NOTE

Drag and drop doesn't seem to work from screen to screen on linux, you need to test with files from the same screen.

I'm trying to implement a drag and drop file upload in angular 8, heavily inspired by this article, and this one.

I cant get any of the drag events to fire. I've checked that the DragDropDirective is imported by using mouseenter methods in the same directive, which launch correctly, i also see that style that i added is correctly applied.

I've been banging my head against this all day, what am i missing?

Edit: This works with files from a Files explorer, but not with files from the desktop. Using Ubuntu 19.

// ponent.html
<section appDragDrop class="panel" id="dropzone-wrapper" (onFileDropped)="uploadFile($event)">
    <input type="file" #fileInput (change)="uploadFile($event.target.files)" accept=".json" hidden>
    <label for="dropzone">
        <span>
            drag and drop here.
        </span>
    </label>
</section>

import { Directive, Output, Input, EventEmitter, HostBinding, HostListener, ElementRef } from '@angular/core';

@Directive({
  selector: '[appDragDrop]'
})
export class DragDropDirective {
  el: ElementRef
  constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }

  // Working fine
  @HostListener('mouseenter') onMouseEnter() {
    console.log('mouse entering')
  }
  
  // Working fine
  @HostListener('mouseleave') onMouseLeave() {
    console.log('mouse leaving')
  }

  // --- Not working ---
  @HostListener('dragover', ['$event']) onDragOver(evt) {
    console.log('A')
  }

  // --- Not working ---
  @HostListener('dragleave', ['$event']) public onDragLeave(evt) {
    console.log('B')
  }

  // --- Not working ---
  @HostListener('drop', ['$event']) public ondrop(evt) {
    console.log('C')
  }
}

NOTE

Drag and drop doesn't seem to work from screen to screen on linux, you need to test with files from the same screen.

Share Improve this question edited Apr 2, 2021 at 10:55 Preston asked Mar 6, 2020 at 13:49 PrestonPreston 8,19711 gold badges63 silver badges94 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 2

The code you have provided works fine: https://stackblitz./edit/angular-ayht81?embed=1&file=src/app/drag-drop.directive.ts

Drop event

Drop will not fire unless you prevent the default behavior on over:

@HostListener("dragover", ["$event"]) onDragOver(evt) {
  evt.preventDefault()
  console.log("A");
}

Proof of work:

Better late than never, but it works for me:

@HostListener("dragover", ["$event"]) onDragOver(event: any) {
  event.preventDefault()
}
@HostListener('drop', ['$event']) onDrop(event: any): boolean {
  console.log("drop", event)
  return false
}

The answer to this issue is usually that you've set preventDefault() and or stopPropagation() elsewhere which is messing with these listeners everywhere on the page.

Here is my go-to directive: Pay attention to preventBodyDrop.

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 dropzone (dropzone)="onDrop($event)"></div>
//
// Any files dropped onto the region are then
// returned as an array of file objects.
// Which in TypeScript is `Array<File>`

@Directive({
  selector: '[libDropzone]',
  standalone: true
})
export class DropZoneDirective {
  // The directive emits a `fileDrop` event
  // with the list of files dropped on the element
  // as a JS array of `File` objects.
  @Output('libDropzone') 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.dragging-over') draggingOver = false;
  @HostBinding('class.dropped') dropped = false;

  // default list of allowed file extensions
  @Input() allowedExts?: ['jpg', 'jpeg', 'png', 'gif']; // iOS HVEC?


  // DRAG AND DROP EVENTS - https://developer.mozilla/en-US/docs/Web/API/HTML_Drag_and_Drop_API
  @HostListener('drop', ['$event'])
  onDrop(event: DragEvent) {
    event.preventDefault();
    this.draggingOver = false;

    const { dataTransfer } = event;

    if (dataTransfer?.items) {
      const files: any[] = [];
      for (let i = 0; i < dataTransfer.items.length; i++) {
        // If dropped items aren't files, reject them
        // TODO: confirm supported image file type (allowedExts)
        // optionally allow allowedExts to override allowed extensions
        if (dataTransfer.items[i].kind === 'file') {
          files.push(dataTransfer.items[i].getAsFile());
        }
      }
      dataTransfer.items.clear();
      if (!!files?.length) this.fileDrop.emit(files);
    } else {
      const files = dataTransfer?.files;
      dataTransfer?.clearData();
      if (!!files?.length) this.fileDrop.emit(Array.from(files));
    }

    this.dropped = true;
    setTimeout(() => {
      this.dropped = false;
    }, 3000);
  }

  @HostListener('dragover', ['$event'])
  onDragOver(event: DragEvent) {
    this.draggingOver = true;
  }

  @HostListener('dragleave', ['$event'])
  onDragLeave(event: DragEvent) {
    this.draggingOver = false;
  }

  // disable native browser file drop on <body>
  @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();
    }
  }
}

If you want to use Drag and Drop event for file upload you should use window:dragenter event listener. This event starts file drag into window and when drag event started then dragover, dragleave and drop will work as well.
Example:

// missing listener which detects that something was dragged into window area
@HostListener('window:dragenter', ['$event'])
onWindowDragEnter(event: any): void {
  event.preventDefault();
  event.stopPropagation();
}

@HostListener('dragover', ['$event'])
onDragOver(event: any): void {
  event.preventDefault();
  event.stopPropagation();
  // ...
}

@HostListener('dragleave', ['$event'])
public onDragLeave(event: any): void {
  event.preventDefault();
  event.stopPropagation();
  // ...
}

@HostListener('drop', ['$event'])
public onDrop(event: any): void {
  event.preventDefault();
  event.stopPropagation();
  // ...
}

However solution wasn't tested on Ubuntu

本文标签: javascriptAngular 8 drag drop dragover dragleave events not firingStack Overflow