projects/rebirth-ng/src/lib/file-upload/file-upload.ts
AfterViewInit
ControlValueAccessor
constructor(rebirthNGConfig: RebirthNGConfig, renderer: Renderer2, http: HttpClient, changeDetectorRef: ChangeDetectorRef)
|
|||||||||||||||
Parameters :
|
errors |
errors:
|
Type : string[]
|
Default value : []
|
fileInput |
fileInput:
|
Type : ElementRef
|
Decorators : ViewChild
|
isUploading |
isUploading:
|
Type : boolean
|
Protected onChange |
onChange:
|
Default value : (_: any) => null
|
Protected onTouched |
onTouched:
|
Default value : () => null
|
selectFiles |
selectFiles:
|
Type : SelectFileModel[]
|
Default value : []
|
addNewFile | ||||
addNewFile($event: )
|
||||
Parameters :
Returns :
void
|
clearErrors |
clearErrors()
|
Returns :
void
|
Private getFileCount |
getFileCount()
|
Returns :
any
|
Private handleFileChoose | ||||||
handleFileChoose(uploadFiles: FileList)
|
||||||
Parameters :
Returns :
void
|
Private httpUploadAllFile | ||||
httpUploadAllFile(files: )
|
||||
Parameters :
Returns :
void
|
Private httpUploadFile | ||||
httpUploadFile(fileItem: )
|
||||
Parameters :
Returns :
any
|
isMoreThanMaxItems |
isMoreThanMaxItems()
|
Returns :
boolean
|
mapFileModel | ||||||
mapFileModel(files: File[])
|
||||||
Parameters :
Returns :
Promise<SelectFileModel[]>
|
newFileChoose | ||||||
newFileChoose(fileInput: HTMLInputElement)
|
||||||
Parameters :
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
onDropFiles | ||||
onDropFiles($event: )
|
||||
Parameters :
Returns :
void
|
Protected onFileUploadError | ||||||
onFileUploadError(fileItem: , error: )
|
||||||
Parameters :
Returns :
Observable<any>
|
Protected onFileUploadSuccess | ||||||
onFileUploadSuccess(fileItem: , res: )
|
||||||
Parameters :
Returns :
Observable<any>
|
onRemoveFile | ||||
onRemoveFile(fileItem: )
|
||||
Parameters :
Returns :
void
|
onRemoveUploadFile | ||||
onRemoveUploadFile(fileItem: )
|
||||
Parameters :
Returns :
void
|
Private onUploadFileChange | ||||
onUploadFileChange(uploadFiles: )
|
||||
Parameters :
Returns :
void
|
registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
Parameters :
Returns :
void
|
registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
Parameters :
Returns :
void
|
removeAllSelectedFiles |
removeAllSelectedFiles()
|
Returns :
void
|
setDisabledState | ||||||
setDisabledState(isDisabled: boolean)
|
||||||
Parameters :
Returns :
void
|
uploadAllFiles |
uploadAllFiles()
|
Returns :
void
|
validFile | ||||||
validFile(file: File)
|
||||||
Parameters :
Returns :
boolean
|
validFiles | ||||||
validFiles(files: File[])
|
||||||
Parameters :
Returns :
File[]
|
Private validFileType | ||||||
validFileType(file: File)
|
||||||
Parameters :
Returns :
any
|
writeValue | ||||||
writeValue(value: any)
|
||||||
Parameters :
Returns :
void
|
accept
|
Type: |
autoUpload
|
Type: |
cancelButton
|
Type: |
canRetry
|
Default value: |
chooseButton
|
Type: |
cssClass
|
Type: |
disabled
|
Type: |
fileSizeErrorMessage
|
Type: |
fileTypeErrorMessage
|
Type: |
imgPreview
|
Type: |
loadingIcon
|
Type: |
maxFileSize
|
Type: |
maxItems
|
Type: |
multiple
|
Default value: |
plusIcon
|
Type: |
previewHeight
|
Type: |
previewTemplate
|
Type: |
previewWidth
|
Type: |
removeIcon
|
Type: |
showErrors
|
Default value: |
toolbarTemplate
|
Type: |
transformResponseUrl
|
Type: |
uploadButton
|
Type: |
uploadFiles
|
Type:
Default value: |
uploadIcon
|
Type: |
uploadParamName
|
Type: |
uploadRequestOptions
|
Type: |
uploadUrl
|
Type: |
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) }));
}));
}
}