UNPKG

ngxc-file-uploader

Version:

Ngxc file uploader is an Angular 9/10 + file uploader

365 lines (359 loc) 17.3 kB
import { EventEmitter, Component, ViewEncapsulation, Input, Output, NgModule } from '@angular/core'; import { HttpEventType, HttpClient, HttpClientModule } from '@angular/common/http'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; class NgxFileUploaderComponent { /** * constructor * * @param {HttpClient} http * */ constructor(http) { this.http = http; this.resetUpload = false; // Outputs this.ApiResponse = new EventEmitter(); this.uploadInitiated = new EventEmitter(); this.everythingDone = new EventEmitter(); this.allowedFiles = []; this.notAllowedFiles = []; this.Caption = []; this.isAllowedFileSingle = true; this.progressBarShow = false; this.enableUploadBtn = false; this.uploadMsg = false; this.afterUpload = false; this.uploadStarted = false; this.currentUploads = []; this.fileNameIndex = true; this.idDate = +new Date(); this.destroy = new Subject(); } /** * ngOnChanges * * @param {SimpleChanges} changes * * @return {void} */ ngOnChanges(changes) { // Track changes in Configuration and see if user has even provided Configuration. if (changes.config && this.config) { // Assign User Configurations to Library Properties. this.theme = this.config.theme || ''; this.id = this.config.id || parseInt((this.idDate / 10000).toString().split('.')[1], 10) + Math.floor(Math.random() * 20) * 10000; this.hideProgressBar = this.config.hideProgressBar || false; this.hideResetBtn = this.config.hideResetBtn || false; this.hideSelectBtn = this.config.hideSelectBtn || false; this.maxSize = (this.config.maxSize || 20) * 1024000; // mb to bytes. this.uploadAPI = this.config.uploadAPI.url; this.method = this.config.uploadAPI.method || 'POST'; this.formatsAllowed = this.config.formatsAllowed || '.jpg,.png,.pdf,.docx,.txt,.gif,.jpeg'; this.multiple = this.config.multiple || false; this.headers = this.config.uploadAPI.headers || {}; this.params = this.config.uploadAPI.params || {}; this.responseType = this.config.uploadAPI.responseType || null; this.fileNameIndex = this.config.fileNameIndex === false ? false : true; this.replaceTexts = { selectFileBtn: this.multiple ? 'Select Files' : 'Select File', resetBtn: 'Reset', uploadBtn: 'Upload', dragNDropBox: 'Drag N Drop', pleaseWaitMessage: 'Please wait until file is uploaded', attachPinBtn: this.multiple ? 'Attach Files...' : 'Attach File...', afterUploadMsg_success: 'Successfully Uploaded !', afterUploadMsg_error: 'Upload Failed !', sizeLimit: 'Size Limit', }; // default replaceText. if (this.config.replaceTexts) { // updated replaceText if user has provided any. this.replaceTexts = Object.assign(Object.assign({}, this.replaceTexts), this.config.replaceTexts); } } // Reset when resetUpload value changes from false to true. if (changes.resetUpload) { if (changes.resetUpload.currentValue === true) { this.resetFileUpload(); } } } /** * ngOnDestroy * * @return {void} */ ngOnDestroy() { this.destroy.next(); this.destroy.complete(); } /** * resetFileUpload * Reset following properties. * * @return {void} */ resetFileUpload() { this.allowedFiles = []; this.Caption = []; this.notAllowedFiles = []; this.uploadMsg = false; this.enableUploadBtn = false; } /** * onChange hook * - Check when user selects files. * * @param {any} event * * @return {void} */ onChange(event) { this.notAllowedFiles = []; const fileExtRegExp = /(?:\.([^.]+))?$/; let fileList; if (this.afterUpload || !this.multiple) { this.allowedFiles = []; this.Caption = []; this.afterUpload = false; } if (event.type === 'drop') { fileList = event.dataTransfer.files; } else { fileList = event.target.files || event.srcElement.files; } // 'forEach' does not exist on 'filelist' that's why this good old 'for' is used. for (let i = 0; i < fileList.length; i++) { const currentFileExt = fileExtRegExp .exec(fileList[i].name)[1] .toLowerCase(); // Get file extension. const isFormatValid = this.formatsAllowed.includes(currentFileExt); const isSizeValid = fileList[i].size <= this.maxSize; // Check whether current file format and size is correct as specified in the configurations. if (isFormatValid && isSizeValid) { this.allowedFiles.push(fileList[i]); } else { this.notAllowedFiles.push({ fileName: fileList[i].name, fileSize: this.convertSize(fileList[i].size), errorMsg: !isFormatValid ? 'Invalid format' : 'Invalid size', }); } } // If there's any allowedFiles. if (this.allowedFiles.length > 0) { this.enableUploadBtn = true; // Upload the files directly if theme is attach pin (as upload btn is not there for this theme). if (this.theme === 'attachPin') { this.uploadFiles(); } } else { this.enableUploadBtn = false; } this.uploadMsg = false; this.uploadStarted = false; this.uploadPercent = 0; event.target.value = null; } /** * uploadFiles * * @return {void} */ uploadFiles() { this.uploadInitiated.emit(true); this.progressBarShow = true; this.uploadStarted = true; this.notAllowedFiles = []; let isError = false; this.isAllowedFileSingle = this.allowedFiles.length <= 1; const formData = new FormData(); // Add data to be sent in this request this.allowedFiles.forEach((file, i) => { formData.append(this.Caption[i] || 'file' + (this.fileNameIndex ? i : ''), this.allowedFiles[i]); }); const options = { headers: this.headers, params: this.params, }; if (this.responseType) { options.responseType = this.responseType; } this.httpCallSubscription = this.http .request(this.method.toUpperCase(), this.uploadAPI, Object.assign({ body: formData, reportProgress: true, observe: 'events' }, options)).pipe(takeUntil(this.destroy)).subscribe((event) => { // Upload Progress if (event.type === HttpEventType.UploadProgress) { this.enableUploadBtn = false; // button should be disabled if process uploading const currentDone = event.loaded / event.total; this.uploadPercent = Math.round((event.loaded / event.total) * 100); } else if (event.type === HttpEventType.Response) { if (event.status === 200 || event.status === 201) { // Success this.progressBarShow = false; this.enableUploadBtn = false; this.uploadStarted = false; this.uploadInitiated.emit(false); this.uploadMsg = true; this.afterUpload = true; if (!isError) { this.uploadMsgText = this.replaceTexts.afterUploadMsg_success; this.uploadMsgClass = 'text-success lead'; } } else { // Failure isError = true; this.handleErrors(); } this.ApiResponse.emit(event); } else { // console.log('Event Other: ', event); } }, (error) => { // Failure isError = true; this.handleErrors(); this.ApiResponse.emit(error); }); } /** * handleErrors * * @return {void} */ handleErrors() { this.progressBarShow = false; this.enableUploadBtn = false; this.uploadMsg = true; this.afterUpload = true; this.uploadMsgText = this.replaceTexts.afterUploadMsg_error; this.uploadMsgClass = 'text-danger lead'; this.uploadStarted = false; this.uploadInitiated.emit(false); } /** * removeFile * * @param {any} i * @param {any} sfNa * * @return {void} */ removeFile(i, sfNa) { if (sfNa === 'sf') { this.allowedFiles.splice(i, 1); this.Caption.splice(i, 1); } else { this.notAllowedFiles.splice(i, 1); } if (this.allowedFiles.length === 0) { this.enableUploadBtn = false; } } /** * convertSize * * @param {number} fileSize * * @return {string} */ convertSize(fileSize) { return fileSize < 1024000 ? (fileSize / 1024).toFixed(2) + ' KB' : (fileSize / 1024000).toFixed(2) + ' MB'; } /** * attachpinOnclick * * @return {void} */ attachpinOnclick() { const element = document.getElementById('sel' + this.id); if (element !== null) { element.click(); } } /** * drop * * @param {any} event * * @return {void} */ drop(event) { event.stopPropagation(); event.preventDefault(); this.onChange(event); } /** * allowDrop * * @param {any} event * * @return {void} */ allowDrop(event) { event.stopPropagation(); event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; } /** * cancelApiCall * * @return {void} */ cancelApiCall() { if (this.httpCallSubscription) { this.httpCallSubscription.unsubscribe(); } } } NgxFileUploaderComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-file-uploader', template: "<div class=\"container\" *ngIf=\"(theme !== 'attachPin')\" id=\"default\">\n\n <!-- Drag n Drop theme Starts -->\n <div *ngIf=\"!uploadStarted && theme == 'dragNDrop'\" id=\"dragNDrop\"\n [ngClass]=\"(hideSelectBtn && hideResetBtn) ? null : 'dragNDropBtmPad'\" class=\"dragNDrop\">\n <div style=\"position:relative;\">\n <div id=\"div1\" class=\"div1 afu-dragndrop-box\" (drop)=\"drop($event)\" (dragover)=\"allowDrop($event)\">\n <p class=\"afu-dragndrop-text\">{{replaceTexts?.dragNDropBox}}</p>\n </div>\n <!-- <span class='label label-info' id=\"upload-file-info{{id}}\">{{allowedFiles[0]?.name}}</span> -->\n </div>\n </div>\n <!-- Drag n Drop theme Ends -->\n <label for=\"sel{{id}}\" class=\"btn btn-primary btn-sm afu-select-btn\"\n *ngIf=\"!uploadStarted && !hideSelectBtn\">{{replaceTexts?.selectFileBtn}}</label>\n <input type=\"file\" id=\"sel{{id}}\" style=\"display: none\" *ngIf=\"!hideSelectBtn\" (change)=\"onChange($event)\"\n title=\"Select file\" name=\"files[]\" [accept]=formatsAllowed [attr.multiple]=\"multiple ? '' : null\" />\n <button class=\"btn btn-info btn-sm resetBtn afu-reset-btn\" (click)=\"resetFileUpload()\"\n *ngIf=\"!hideResetBtn\">{{replaceTexts?.resetBtn}}</button>\n <br *ngIf=\"!hideSelectBtn\">\n <p class=\"constraints-info afu-constraints-info\">({{formatsAllowed}}) {{replaceTexts?.sizeLimit}}: {{(convertSize(maxSize))}}\n </p>\n <!--Allowed file list-->\n <div class=\"row afu-valid-file\" *ngFor=\"let sf of allowedFiles;let i=index\">\n <p class=\"col-xs-3 textOverflow\"><span class=\"text-primary\">{{sf.name}}</span></p>\n <p class=\"col-xs-3 padMarg sizeC\"><strong>({{convertSize(sf.size)}})</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>\n <!-- <input class=\"col-xs-3 progress caption\" type=\"text\" placeholder=\"Caption..\" [(ngModel)]=\"Caption[i]\" *ngIf=\"!uploadStarted\"/> -->\n <div class=\"progress col-xs-3 padMarg afu-progress-bar\" *ngIf=\"isAllowedFileSingle && progressBarShow && !hideProgressBar\">\n <span class=\"progress-bar progress-bar-success\" role=\"progressbar\"\n [ngStyle]=\"{'width':uploadPercent+'%'}\">{{uploadPercent}}%</span>\n </div>\n <a class=\"col-xs-1\" role=\"button\" (click)=\"removeFile(i,'sf')\" *ngIf=\"!uploadStarted\"><i class=\"fa fa-times\"></i></a>\n </div>\n <!--Not Allowed file list-->\n <div class=\"row text-danger afu-invalid-file\" *ngFor=\"let na of notAllowedFiles;let j=index\">\n <p class=\"col-xs-3 textOverflow\"><span>{{na['fileName']}}</span></p>\n <p class=\"col-xs-3 padMarg sizeC\"><strong>({{na['fileSize']}})</strong></p>\n <p class=\"col-xs-3 \">{{na['errorMsg']}}</p>\n <a class=\"col-xs-1 delFileIcon\" role=\"button\" (click)=\"removeFile(j,'na')\" *ngIf=\"!uploadStarted\">&nbsp;<i\n class=\"fa fa-times\"></i></a>\n </div>\n\n <p *ngIf=\"uploadMsg\" class=\"{{uploadMsgClass}} + afu-upload-status\">{{uploadMsgText}}<p>\n <div *ngIf=\"!isAllowedFileSingle && progressBarShow && !hideProgressBar\">\n <div class=\"progress col-xs-4 padMarg afu-progress-bar\">\n <span class=\"progress-bar progress-bar-success\" role=\"progressbar\"\n [ngStyle]=\"{'width':uploadPercent+'%'}\">{{uploadPercent}}%</span>\n </div>\n <br />\n <br />\n </div>\n <button *ngIf=\"!uploadStarted && enableUploadBtn\" class=\"btn btn-success afu-upload-btn\" type=\"button\" (click)=\"uploadFiles()\"\n >{{replaceTexts?.uploadBtn}}</button>\n <div *ngIf=\"uploadStarted\" class=\"uploading-message\" >{{replaceTexts?.pleaseWaitMessage}}</div>\n <br>\n</div>\n\n<!--/////////////////////////// ATTACH PIN THEME //////////////////////////////////////////////////////////-->\n<div *ngIf=\"theme == 'attachPin'\" id=\"attachPin\">\n <div style=\"position:relative;padding-left:6px\">\n <a class='btn up_btn afu-attach-pin' (click)=\"attachpinOnclick()\">\n {{replaceTexts?.attachPinBtn}}\n <i class=\"fa fa-paperclip\" aria-hidden=\"true\"></i>\n <!-- <p style=\"margin-top:10px\">({{formatsAllowed}}) Size limit- {{(convertSize(maxSize))}}</p> -->\n <input type=\"file\" id=\"sel{{id}}\" (change)=\"onChange($event)\" style=\"display: none\" title=\"Select file\"\n name=\"files[]\" [accept]=formatsAllowed [attr.multiple]=\"multiple ? '' : null\" />\n <br>\n </a>\n &nbsp;\n <span class='label label-info' id=\"upload-file-info{{id}}\">{{allowedFiles[0]?.name}}</span>\n </div>\n</div>\n\n", encapsulation: ViewEncapsulation.None, styles: [".constraints-info{font-style:italic;margin-top:10px}.padMarg{margin-bottom:0;padding:0}.caption{margin-right:5px}.textOverflow{overflow:hidden;padding-right:0;text-overflow:ellipsis;white-space:nowrap}.up_btn{background-color:transparent;border:2px solid #5c5b5b;border-radius:22px;color:#000}.delFileIcon{color:#ce0909;text-decoration:none}.uploading-message{margin:15px 0}.dragNDrop .div1{border:2px dashed #5c5b5b;display:border-box;height:6rem;width:20rem}.dragNDrop .div1>p{color:#5c5b5b;font-weight:700;margin-top:1.4em;text-align:center}.dragNDropBtmPad{padding-bottom:2rem}@media screen and (max-width:620px){.caption{padding:0}}@media screen and (max-width:510px){.sizeC{width:25%}}@media screen and (max-width:260px){.caption,.sizeC{font-size:10px}}.resetBtn{margin-left:3px}"] },] } ]; NgxFileUploaderComponent.ctorParameters = () => [ { type: HttpClient } ]; NgxFileUploaderComponent.propDecorators = { config: [{ type: Input }], resetUpload: [{ type: Input }], ApiResponse: [{ type: Output }], uploadInitiated: [{ type: Output }], everythingDone: [{ type: Output }] }; class NgxFileUploaderModule { } NgxFileUploaderModule.decorators = [ { type: NgModule, args: [{ imports: [ CommonModule, HttpClientModule, ], declarations: [NgxFileUploaderComponent], exports: [NgxFileUploaderComponent] },] } ]; /* * Public API Surface of ngx-file-uploader */ /** * Generated bundle index. Do not edit. */ export { NgxFileUploaderComponent, NgxFileUploaderModule }; //# sourceMappingURL=ngxc-file-uploader.js.map