src/lib/file-upload.method.ts
Properties |
|
Methods |
|
Accessors |
| Protected addInputToDom | ||||||
addInputToDom(accept: string)
|
||||||
|
Defined in src/lib/file-upload.method.ts:113
|
||||||
|
Parameters :
Returns :
HTMLInputElement
|
| Public call | ||||||
call(parameters?: FileUploadMethodParameters)
|
||||||
|
Defined in src/lib/file-upload.method.ts:37
|
||||||
|
Parameters :
Returns :
Promise<File | null>
|
| Protected getDocument |
getDocument()
|
|
Defined in src/lib/file-upload.method.ts:122
|
|
Returns :
Document
|
| Protected handleFileInputChange | ||||||
handleFileInputChange(event: any)
|
||||||
|
Defined in src/lib/file-upload.method.ts:62
|
||||||
|
Parameters :
Returns :
File | null
|
| Protected readFile | ||||||
readFile(file: File)
|
||||||
|
Defined in src/lib/file-upload.method.ts:84
|
||||||
|
Parameters :
Returns :
Promise<File>
|
| Protected removeInputFromDom |
removeInputFromDom()
|
|
Defined in src/lib/file-upload.method.ts:77
|
|
Returns :
void
|
| Public setDocument | ||||||
setDocument(document: Document)
|
||||||
|
Defined in src/lib/file-upload.method.ts:58
|
||||||
|
Parameters :
Returns :
void
|
| Protected _document |
Type : Document | null
|
Default value : null
|
|
Defined in src/lib/file-upload.method.ts:28
|
| Protected _fileInput |
Type : HTMLInputElement | null
|
Default value : null
|
|
Defined in src/lib/file-upload.method.ts:26
|
| Public progress$ |
Type : Subject<number>
|
Default value : new Subject<number>()
|
|
Defined in src/lib/file-upload.method.ts:24
|
| document |
getdocument()
|
|
Defined in src/lib/file-upload.method.ts:30
|
import { Injectable } from '@angular/core';
import { Method } from '@rxap/pattern';
import {
firstValueFrom,
fromEvent,
of,
race,
Subject,
} from 'rxjs';
import {
map,
switchMap,
take,
} from 'rxjs/operators';
export interface FileUploadMethodParameters {
accept?: string;
fileInput?: HTMLInputElement;
}
@Injectable()
export class FileUploadMethod implements Method<File | null, FileUploadMethodParameters> {
public progress$: Subject<number> = new Subject<number>();
protected _fileInput: HTMLInputElement | null = null;
protected _document: Document | null = null;
protected get document(): Document {
if (!this._document) {
this._document = this.getDocument();
}
return this._document;
}
public call(parameters?: FileUploadMethodParameters): Promise<File | null> {
const accept = parameters?.accept ?? '**/**';
const fileInput = parameters?.fileInput ?? this.addInputToDom(accept);
fileInput.setAttribute('accept', accept);
const change$ = fromEvent(fileInput, 'change').pipe(
map(event => this.handleFileInputChange(event)),
switchMap(file => file ? this.readFile(file) : of(null)),
take(1),
);
const cancel$ = fromEvent(this.document.body, 'focus').pipe(take(1), map(() => null));
fileInput.click();
return firstValueFrom(race(
change$,
cancel$,
));
}
public setDocument(document: Document) {
this._document = document;
}
protected handleFileInputChange(event: any): File | null {
this.removeInputFromDom();
const files: { [key: string]: File } = event?.target?.files as any ?? {};
if (Object.keys(files).length !== 1) {
throw new Error('File upload failed');
}
let file: File | null = null;
for (const key in files) {
if (!isNaN(parseInt(key, 10))) {
file = files[key];
}
}
return file;
}
protected removeInputFromDom(): void {
if (this._fileInput) {
this.document.body.removeChild(this._fileInput);
}
this._fileInput = null;
}
protected readFile(file: File): Promise<File> {
const reader = new FileReader();
const loadFile$ = fromEvent<any>(reader, 'load').pipe(
map(event => new File(
[ event.target.result ],
file.name,
{
type: file.type,
lastModified: file.lastModified,
},
)),
take(1),
);
fromEvent(reader, 'progress').pipe(
map((progress: Event) => {
if (progress instanceof ProgressEvent) {
return progress.loaded / progress.total * 100;
}
throw new Error('The progress event is not an instance of ProgressEvent');
}),
).subscribe(this.progress$);
reader.readAsArrayBuffer(file);
return firstValueFrom(loadFile$);
}
protected addInputToDom(accept: string): HTMLInputElement {
const fileInput = this.document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.setAttribute('style', 'display: none');
fileInput.setAttribute('accept', accept);
this.document.body.appendChild(fileInput);
return this._fileInput = fileInput;
}
protected getDocument(): Document {
return window.document;
}
}