import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Directive, EventEmitter, Host, HostBinding, HostListener, Input, Optional, Output } from '@angular/core'; import { NgModel } from '@angular/forms'; @Directive({ selector: '[prutechFileSelect]', }) export class NuFileSelectDirective { private _multiple: boolean = false; /** * fileSelect?: function * Event emitted when a file or files are selected in host [HTMLInputElement]. * Emits a [FileList | File] object. * Alternative to not use [(ngModel)]. */ @Output() fileSelect: EventEmitter = new EventEmitter(); constructor(@Optional() @Host() private model: NgModel) { } /** * multiple?: boolean * Sets whether multiple files can be selected at once in host element, or just a single file. * Can also be 'multiple' native attribute. */ @Input('multiple') set multiple(multiple: boolean) { this._multiple = coerceBooleanProperty(multiple); } /** * Binds native 'multiple' attribute if [multiple] property is 'true'. */ @HostBinding('attr.multiple') get multipleBinding(): string { return this._multiple ? '' : undefined; } /** * Listens to 'change' host event to get [HTMLInputElement] files. * Emits the 'fileSelect' event with a [FileList] or [File] depending if 'multiple' attr exists in host. * Uses [(ngModel)] if declared, instead of emitting 'fileSelect' event. */ @HostListener('change', ['$event']) onChange(event: Event): void { if (event.target instanceof HTMLInputElement) { const fileInputEl: HTMLInputElement = event.target; const files: FileList = fileInputEl.files; if (files.length) { const value: FileList | File = this._multiple ? (files.length > 1 ? files : files[0]) : files[0]; this.model ? this.model.update.emit(value) : this.fileSelect.emit(value); } } } }