Я создал директиву загрузки файлов с функцией перетаскивания. Странно то, что он работает, когда файл выбирается из открытого окна, но не работает, когда файл перетаскивается, даже если вся логика одинакова и переменные имеют одинаковые значения.
Код ниже не генерирует событие, даже если код выполняется до строки .emit
@Output() public fileSelect: EventEmitter<File> = new EventEmitter<File>();
@HostListener('drop', ['$event'])
public onDrop(event: any): void {
console.log("drop");
const transfer = this.getDataTransfer(event);
if (!transfer) {
return;
}
this.preventAndStop(event);
this.emitFileOver(false);
this.handleFiles(transfer.files);
}
private emitFileSelect(file: any): void {
this.fileSelect.emit(file);
}
Разница между перетаскиванием и выбором файла из окна только в слушателе. Код ниже открывает окна выбора, и когда файл выбирается, вызывается handleFiles, точно так же, как в событии перетаскивания.
@HostListener('change', ['$event'])
public onChange(event) {
console.log("change");
this.handleFiles(event.srcElement.files);
}
как вы можете видеть, оба слушателя вызывают handleFiles с одинаковым значением параметра (я его отлаживал).
Весь код:
import { FileUploadState, FileOptions } from './file-drop.models';
import {
Directive,
EventEmitter,
ElementRef,
HostListener,
Input,
Output
} from '@angular/core';
@Directive({ selector: '[pdFileDrop]' })
export class FileDropDirective {
@Output()
public fileOver: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output()
public fileUploadState: EventEmitter<FileUploadState> = new EventEmitter<
FileUploadState
>();
@Output() public fileSelect: EventEmitter<File> = new EventEmitter<File>();
@Input() public options: FileOptions;
private element: ElementRef;
public constructor(element: ElementRef) {
this.element = element;
this.element.nativeElement.setAttribute('multiple', 'multiple');
}
@HostListener('dragover', ['$event'])
public onDragOver(event: any): void {
console.log("dragOver");
const transfer = this.getDataTransfer(event);
if (!this.haveFiles(transfer.types)) {
return;
}
transfer.dropEffect = 'copy';
this.preventAndStop(event);
this.emitFileOver(true);
}
@HostListener('dragleave', ['$event'])
public onDragLeave(event: any): void {
console.log("dragLeave");
if (event.currentTarget === (this as any).element[0]) {
return;
}
this.preventAndStop(event);
this.emitFileOver(false);
}
@HostListener('drop', ['$event'])
public onDrop(event: any): void {
console.log("drop");
const transfer = this.getDataTransfer(event);
if (!transfer) {
return;
}
this.preventAndStop(event);
this.emitFileOver(false);
this.handleFiles(transfer.files);
}
@HostListener('change', ['$event'])
public onChange(event) {
console.log("change");
this.handleFiles(event.srcElement.files);
}
ngOnInit() {
console.log("Observers: " + this.fileSelect.observers.length);
}
private handleFiles(files: any) {
console.log("handleFiles");
// check all files
for (let i = 0; i < files.length; i++) {
console.log(i);
const fileUploadState = <FileUploadState>{};
const isFileTypeSupported = this.isFileTypeSupported(files[i].name);
const isFileSizeAllowed = this.isFileSizeAllowed(files[i].size);
fileUploadState.fileTypeNotAllowedError = !isFileTypeSupported;
fileUploadState.fileSizeToBigError = !isFileSizeAllowed;
fileUploadState.error = !isFileTypeSupported || !isFileSizeAllowed;
fileUploadState.done = false;
fileUploadState.file = files[i];
this.emitFileSelect(fileUploadState);
}
}
private emitFileOver(isOver: boolean): void {
this.fileOver.emit(isOver);
}
private emitFileSelect(file: any): void {
this.fileSelect.emit(file);
}
private isFileTypeSupported(fileName: string): boolean {
if (!this.options) {
return true;
} else if (!this.options.supportedFilesTypes) {
return true;
} else {
return (
this.options.supportedFilesTypes.indexOf(
this.getFileExtensionFromFileName(fileName).toLowerCase()
) !== -1
);
}
}
private isFileSizeAllowed(fileSize: number) {
if (!this.options) {
return true;
} else if (!this.options.maxUploadSizeInMb) {
return true;
} else {
return fileSize / 1000000 <= this.options.maxUploadSizeInMb;
}
}
private getDataTransfer(event: any | any): DataTransfer {
return event.dataTransfer
? event.dataTransfer
: event.originalEvent.dataTransfer;
}
private preventAndStop(event: any): void {
event.preventDefault();
event.stopPropagation();
}
private getFileExtensionFromFileName(fileName: string): string {
return fileName.substr(fileName.lastIndexOf('.') + 1);
}
private haveFiles(types: any): boolean {
if (!types) {
return false;
}
if (types.indexOf) {
return types.indexOf('Files') !== -1;
}
if (types.contains) {
return types.contains('Files');
}
return false;
}
}
Испускаемое событие отлавливается в компоненте. В случае, когда файл выбран, выполнение кода запускается, как и ожидалось, в эту функцию fileSelect ($ event) (см. Ниже), в случае drg & drop выполнение кода заканчивается в строке .emit функции emitFileSelect (см. Выше).
<label pdFileDrop [options]="options" (change)="fileSelect($event)">
<input type="file" />
</label>
fileSelect($event) {
const that = this;
const reader = new FileReader();
const image = $event.target.closest('.box').querySelectorAll('img')[0];
$event.target.closest('.box').classList.add('thumb-shown');
reader.onload = function (event) {
if (event && event.target && event.target['result']) {
image.src = event.target['result'];
that._file.id = that.id;
that._file.src = event.target['result'];
that.attachmentForm.controls['fileName'].patchValue(
$event.target.files[0].name
);
that._file.description = $event.target.files[0].name; //fileName;
that._file.rotation = that.rotateDeg;
that._file.thumbnail = image.src;
that._file.fileType = that.getFileType(image.src);
}
};
Может быть, функция protectAndStop, которая запрещает браузеру перенаправлять на путь удаленного файла и показывать файл в браузере, также блокирует событие .emit? ОБНОВЛЕНИЕ: функция protectAndStop не блокирует event.emit.