File

projects/rebirth-ng/src/lib/file-upload/file-upload.ts

Implements

AfterViewInit ControlValueAccessor

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(rebirthNGConfig: RebirthNGConfig, renderer: Renderer2, http: HttpClient, changeDetectorRef: ChangeDetectorRef)
Parameters :
Name Type Optional
rebirthNGConfig RebirthNGConfig no
renderer Renderer2 no
http HttpClient no
changeDetectorRef ChangeDetectorRef no

Properties

errors
errors: string[]
Type : string[]
Default value : []
fileInput
fileInput: ElementRef
Type : ElementRef
Decorators : ViewChild
isUploading
isUploading: boolean
Type : boolean
Protected onChange
onChange:
Default value : (_: any) => null
Protected onTouched
onTouched:
Default value : () => null
selectFiles
selectFiles: SelectFileModel[]
Type : SelectFileModel[]
Default value : []

Methods

addNewFile
addNewFile($event: )
Parameters :
Name Optional
$event no
Returns : void
clearErrors
clearErrors()
Returns : void
Private getFileCount
getFileCount()
Returns : any
Private handleFileChoose
handleFileChoose(uploadFiles: FileList)
Parameters :
Name Type Optional
uploadFiles FileList no
Returns : void
Private httpUploadAllFile
httpUploadAllFile(files: )
Parameters :
Name Optional
files no
Returns : void
Private httpUploadFile
httpUploadFile(fileItem: )
Parameters :
Name Optional
fileItem no
Returns : any
isMoreThanMaxItems
isMoreThanMaxItems()
Returns : boolean
mapFileModel
mapFileModel(files: File[])
Parameters :
Name Type Optional
files File[] no
newFileChoose
newFileChoose(fileInput: HTMLInputElement)
Parameters :
Name Type Optional
fileInput HTMLInputElement no
Returns : void
ngAfterViewInit
ngAfterViewInit()
Returns : void
onDropFiles
onDropFiles($event: )
Parameters :
Name Optional
$event no
Returns : void
Protected onFileUploadError
onFileUploadError(fileItem: , error: )
Parameters :
Name Optional
fileItem no
error no
Returns : Observable<any>
Protected onFileUploadSuccess
onFileUploadSuccess(fileItem: , res: )
Parameters :
Name Optional
fileItem no
res no
Returns : Observable<any>
onRemoveFile
onRemoveFile(fileItem: )
Parameters :
Name Optional
fileItem no
Returns : void
onRemoveUploadFile
onRemoveUploadFile(fileItem: )
Parameters :
Name Optional
fileItem no
Returns : void
Private onUploadFileChange
onUploadFileChange(uploadFiles: )
Parameters :
Name Optional
uploadFiles no
Returns : void
registerOnChange
registerOnChange(fn: any)
Parameters :
Name Type Optional
fn any no
Returns : void
registerOnTouched
registerOnTouched(fn: any)
Parameters :
Name Type Optional
fn any no
Returns : void
removeAllSelectedFiles
removeAllSelectedFiles()
Returns : void
setDisabledState
setDisabledState(isDisabled: boolean)
Parameters :
Name Type Optional
isDisabled boolean no
Returns : void
uploadAllFiles
uploadAllFiles()
Returns : void
validFile
validFile(file: File)
Parameters :
Name Type Optional
file File no
Returns : boolean
validFiles
validFiles(files: File[])
Parameters :
Name Type Optional
files File[] no
Returns : File[]
Private validFileType
validFileType(file: File)
Parameters :
Name Type Optional
file File no
Returns : any
writeValue
writeValue(value: any)
Parameters :
Name Type Optional
value any no
Returns : void

Inputs

accept

Type: string

autoUpload

Type: boolean

cancelButton

Type: string

canRetry

Default value: true

chooseButton

Type: string

cssClass

Type: string

disabled

Type: boolean

fileSizeErrorMessage

Type: string

fileTypeErrorMessage

Type: string

imgPreview

Type: boolean

loadingIcon

Type: string

maxFileSize

Type: number

maxItems

Type: number

multiple

Default value: true

plusIcon

Type: string

previewHeight

Type: string

previewTemplate

Type: TemplateRef<any>

previewWidth

Type: string

removeIcon

Type: string

showErrors

Default value: true

toolbarTemplate

Type: TemplateRef<any>

transformResponseUrl

Type: function

uploadButton

Type: string

uploadFiles

Type: SelectFileModel[]

Default value: []

uploadIcon

Type: string

uploadParamName

Type: string

uploadRequestOptions

Type: any

uploadUrl

Type: string

Outputs

fileUploadCompleted $event type: EventEmitter
fileUploadError $event type: EventEmitter
fileUploadStart $event type: EventEmitter
fileUploadSuccess $event type: EventEmitter
removeFiles $event type: EventEmitter
selectFilesChange $event type: EventEmitter
uploadFilesChange $event type: EventEmitter
import {
  ViewChild,
  ElementRef,
  Input,
  AfterViewInit,
  Renderer2,
  Output,
  EventEmitter,
  ChangeDetectorRef,
  TemplateRef,
} from '@angular/core';
import { SelectFileModel } from './file-upload.model';
import { readFileAsDataURL } from '../utils/dom-utils';
import { formatFileSize, formatString, noop } from '../utils/lange-utils';
import { RebirthNGConfig } from '../rebirth-ng.config';
import { HttpClient } from '@angular/common/http';
import { map, catchError } from 'rxjs/operators';
import { Observable, of, forkJoin } from 'rxjs';
import { ControlValueAccessor } from '@angular/forms';

export class FileUpload implements AfterViewInit, ControlValueAccessor {

  @Input() accept: string;
  @Input() multiple = true;
  @Input() autoUpload: boolean;
  @Input() showErrors = true;
  @Input() maxItems: number;
  @Input() maxFileSize: number; // bytes
  @Input() uploadUrl: string;
  @Input() uploadParamName: string;
  @Input() uploadRequestOptions: any;
  @Input() imgPreview: boolean;
  @Input() previewWidth: string;
  @Input() previewHeight: string;
  @Input() cssClass: string;
  @Input() fileSizeErrorMessage: string;
  @Input() fileTypeErrorMessage: string;
  @Input() chooseButton: string;
  @Input() uploadButton: string;
  @Input() cancelButton: string;
  @Input() plusIcon: string;
  @Input() uploadIcon: string;
  @Input() loadingIcon: string;
  @Input() removeIcon: string;
  @Input() canRetry = true;
  @Input() toolbarTemplate: TemplateRef<any>;
  @Input() previewTemplate: TemplateRef<any>;
  @Input() disabled: boolean;
  @Output() selectFilesChange = new EventEmitter<SelectFileModel[]>();
  @Output() fileUploadStart = new EventEmitter<SelectFileModel[]>();
  @Output() fileUploadCompleted = new EventEmitter<SelectFileModel[]>();
  @Output() fileUploadSuccess = new EventEmitter<SelectFileModel>();
  @Output() fileUploadError = new EventEmitter<SelectFileModel>();
  @Output() removeFiles = new EventEmitter<SelectFileModel[]>();
  @Output() uploadFilesChange = new EventEmitter<SelectFileModel[]>();
  @Input() uploadFiles: SelectFileModel[] = [];
  @Input() transformResponseUrl: (res: any) => string;
  @ViewChild('file') fileInput: ElementRef;
  selectFiles: SelectFileModel[] = [];
  isUploading: boolean;
  errors: string[] = [];
  protected onChange = (_: any) => null;
  protected onTouched = () => null;

  constructor(protected rebirthNGConfig: RebirthNGConfig,
              protected renderer: Renderer2,
              protected http: HttpClient,
              protected changeDetectorRef: ChangeDetectorRef) {

    this.fileSizeErrorMessage = this.rebirthNGConfig.fileUpload.fileSizeErrorMessage;
    this.fileTypeErrorMessage = this.rebirthNGConfig.fileUpload.fileTypeErrorMessage;
    this.uploadParamName = this.rebirthNGConfig.fileUpload.uploadParamName;
    this.previewWidth = this.rebirthNGConfig.fileUpload.previewWidth;
    this.previewHeight = this.rebirthNGConfig.fileUpload.previewHeight;
    this.imgPreview = this.rebirthNGConfig.fileUpload.imgPreview;
    this.chooseButton = this.rebirthNGConfig.fileUpload.chooseButton;
    this.uploadButton = this.rebirthNGConfig.fileUpload.uploadButton;
    this.cancelButton = this.rebirthNGConfig.fileUpload.cancelButton;
    this.plusIcon = this.rebirthNGConfig.fileUpload.plusIcon;
    this.uploadIcon = this.rebirthNGConfig.fileUpload.uploadIcon;
    this.loadingIcon = this.rebirthNGConfig.fileUpload.loadingIcon;
    this.removeIcon = this.rebirthNGConfig.fileUpload.removeIcon;
    this.showErrors = this.rebirthNGConfig.fileUpload.showErrors;
    this.transformResponseUrl = this.rebirthNGConfig.fileUpload.transformResponseUrl;

  }

  ngAfterViewInit(): void {
    if (this.accept) {
      this.renderer.setProperty(this.fileInput.nativeElement, 'accept', this.accept);
    }

    this.renderer.setProperty(this.fileInput.nativeElement, 'multiple', this.multiple);
  }

  writeValue(value: any): void {
    this.uploadFiles = value || [];
    this.changeDetectorRef.markForCheck();
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  isMoreThanMaxItems() {
    const fileSize = this.getFileCount();
    if (!this.multiple) {
      return fileSize >= 1;
    }
    return this.maxItems && (fileSize >= this.maxItems);
  }

  clearErrors() {
    this.errors = [];
  }

  onDropFiles($event) {
    if (this.disabled) {
      return;
    }
    $event.stopPropagation();
    $event.preventDefault();
    this.clearErrors();
    const files = $event.dataTransfer.files;
    if (files && files.length) {
      this.handleFileChoose(files);
    }
  }

  addNewFile($event) {
    if (this.disabled) {
      return;
    }
    $event.stopPropagation();
    if (!this.isMoreThanMaxItems()) {
      this.fileInput.nativeElement.value = null;
      this.fileInput.nativeElement.click(); // simulate file input event
    }
  }

  newFileChoose(fileInput: HTMLInputElement) {
    this.clearErrors();
    if (fileInput.files && fileInput.files.length) {
      this.handleFileChoose(fileInput.files);
    }
  }

  removeAllSelectedFiles() {
    this.onTouched();
    const files = this.selectFiles;
    this.selectFiles = [];
    this.clearErrors();
    this.removeFiles.emit(files);
  }

  onRemoveFile(fileItem) {
    if (this.disabled) {
      return;
    }
    this.onTouched();
    this.selectFiles = this.selectFiles.filter(item => item !== fileItem);
    this.removeFiles.emit([fileItem]);
  }

  onRemoveUploadFile(fileItem) {
    if (this.disabled) {
      return;
    }
    this.onTouched();
    this.onUploadFileChange(this.uploadFiles.filter(item => item !== fileItem));
    this.removeFiles.emit([fileItem]);
  }

  uploadAllFiles() {
    this.clearErrors();
    this.httpUploadAllFile(this.selectFiles);
  }

  private httpUploadAllFile(files) {
    this.isUploading = true;
    this.fileUploadStart.emit(files);
    const subscriptions = files.map(fileItem => this.httpUploadFile(fileItem));
    forkJoin(subscriptions)
      .subscribe(noop, noop, () => {
        this.isUploading = false;
        this.fileUploadCompleted.emit(files);
        this.onTouched();
      });
  }

  private httpUploadFile(fileItem) {
    const formData = new FormData();
    formData.append(this.uploadParamName, fileItem.file);
    return this.http.post(this.uploadUrl, formData, this.uploadRequestOptions)
      .pipe(
        map((res) => this.onFileUploadSuccess(fileItem, res)),
        catchError((error) => this.onFileUploadError(fileItem, error))
      );
  }

  protected onFileUploadSuccess(fileItem, res): Observable<any> {
    fileItem.uploadResponse = res;
    fileItem.url = this.transformResponseUrl(res) || fileItem.url;
    this.fileUploadSuccess.emit(fileItem);
    this.selectFiles = this.selectFiles.filter(item => item !== fileItem);
    this.onUploadFileChange([...(this.uploadFiles || []), fileItem]);
    this.changeDetectorRef.markForCheck();
    return of({ result: res, success: true });
  }

  private onUploadFileChange(uploadFiles) {
    this.uploadFiles = uploadFiles;
    this.uploadFilesChange.emit(uploadFiles);
    this.onChange(uploadFiles);
  }

  protected onFileUploadError(fileItem, error): Observable<any> {
    this.errors.push(`${fileItem.name}: ${error.error || error.statusText}`);
    this.fileUploadError.emit({
      name: fileItem.name,
      displaySize: fileItem.displaySize,
      url: fileItem.url,
      file: fileItem.file,
      uploadResponse: error
    });

    if (!this.canRetry) {
      this.onRemoveFile(fileItem);
    }

    this.changeDetectorRef.markForCheck();
    return of({ error: error, success: false });
  }

  private handleFileChoose(uploadFiles: FileList) {
    const files = this.validFiles(Array.from(uploadFiles));
    this.mapFileModel(files)
      .then(fileModels => {
        this.selectFiles = [...this.selectFiles, ...fileModels];
        this.selectFilesChange.emit(this.selectFiles);
        this.changeDetectorRef.markForCheck();
        return fileModels || [];
      })
      .then((fileModels) => {
        if (this.autoUpload) {
          return this.httpUploadAllFile(fileModels);
        }
      });
  }

  validFiles(files: File[]): File[] {
    const fileCount = this.getFileCount();
    const size = this.multiple ? this.maxItems : 1;
    if (size && (fileCount + files.length > size)) {
      files = files.slice(0, size - fileCount);
    }

    return files.filter(file => {
      return this.validFile(file);
    });
  }

  validFile(file: File): boolean {
    const errors = [];
    if (this.maxFileSize && file.size > this.maxFileSize) {
      errors.push(formatString(this.fileSizeErrorMessage, file.name, formatFileSize(this.maxFileSize)));
    }

    if (this.accept && !this.validFileType(file)) {
      errors.push(formatString(this.fileTypeErrorMessage, file.name, this.accept));
    }

    this.errors.push(...errors);
    if (this.errors.length) {
      this.fileUploadError.emit({
        name: file.name,
        file: file,
        uploadResponse: this.errors
      });
    }
    return !errors.length;
  }

  private getFileCount() {
    return ((this.selectFiles || []).length + (this.uploadFiles || []).length);
  }

  private validFileType(file: File) {
    return this.accept.split(',').some(type => {
      return new RegExp(`^${type.replace(/\*/g, '.*')}$`).test(file.type);
    });
  }

  mapFileModel(files: File[]): Promise<SelectFileModel[]> {
    return Promise.all(files.map(file => {
      return readFileAsDataURL(file)
        .then(url => ({ url, name: file.name, file, displaySize: formatFileSize(file.size) }));
    }));

  }
}

results matching ""

    No results matching ""