'File upload in angular 11
I am starting on angular 11 and am trying to upload a file or multiple files as a post request to my back end API ( that I created in node.js ) Here is the code I used :
web-request.service.ts
import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class WebRequestService {
readonly ROOT_URL;
constructor(private http:HttpClient) {
this.ROOT_URL= 'http://localhost:4000'
}
// observable
get(url:string){
return this.http.get(`${this.ROOT_URL}/${url}`)
}
post(url:string,payload:Object){
return this.http.post(`${this.ROOT_URL}/${url}`,payload)
}
patch(url:string,payload:Object){
return this.http.patch(`${this.ROOT_URL}/${url}`,payload)
}
delete (url:string){
return this.http.delete(`${this.ROOT_URL}/${url}`)
}
}
files.service.ts
import { Injectable } from '@angular/core';
import {WebRequestService} from "./web-request.service";
@Injectable({
providedIn: 'root'
})
export class FilesService {
constructor(private webReqService:WebRequestService) { }
// file to upload should be passed
postFile(fileToUpload: File): Observable<boolean> {
// we want to send a post web request to upload the file
const endpoint = '/upload-txt';
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
this.webReqService.post(endpoint, formData, { headers: yourHeadersConfig })
.map(() => { return true; })
.catch((e) => this.handleError(e));
}
ReadTable(){
this.webReqService.get('/read-table-csv')
}
}
file-upload.component.html
<div class="file is-primary">
<label class="file-label">
<input
type="file"
id="file"
(change)="handleFileInput($event.target.files)"
/>
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">Browse Files </span>
</span>
</label>
</div>
file-upload.component.ts
import { Component, OnInit } from '@angular/core';
import {FilesService} from "../../files.service"
@Component({
selector: 'app-files-upload',
templateUrl: './files-upload.component.html',
styleUrls: ['./files-upload.component.scss']
})
export class FilesUploadComponent implements OnInit {
fileToUpload: File | null;
files: File[] = [];
constructor(private fileUploadService: FilesService) {
}
ngOnInit(): void {
}
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
}
uploadFileToActivity() {
this.fileUploadService.postFile(this.fileToUpload).subscribe(data => {
// do something, if upload success
}, error => {
console.log(error);
});
}
}
}
How do I fix it, and is there a way I can have the files upload as a drag and drop option as well ?
Solution 1:[1]
You can use drag/drop events. Here's how I did it in a custom Component:
HTML
<div
id="drop-zone"
(drop)="onFileDrop($event)"
(dragover)="onDrag($event)"
(dragleave)="onDragEnd($event)"
(drop)="onDragEnd($event)">
<strong class="message">{{_dropZoneMsg}}</strong>
<input type="file"
[style.cursor]="_disabled?'auto':'pointer'"
[accept]="_accept"
[disabled]="_disabled"
[multiple]="_multiple"
(change)="onFileClick(uploadsInput.files)"
[title]="' '"
#uploadsInput>
</div>
TS
export class DropZoneComponent implements OnInit, OnChanges {
/** What files to allow. Default = '*' (All file types)*/
@Input('accept') _accept = '*'
/** Allow multiple files to be dropped. Default = true */
@Input('multiple') _multiple = true
/** All click to add files. Default = true */
@Input('clickable') _clickable = true
/** What to say before file is dragged over drop zone. Default = 'Click or Drag & Drop to add files' */
@Input('dropZoneStartMsg') _dropZoneStartMsg = 'Click or Drag & Drop to add files'
/** What to say when file is hovering over drop zone. Default = 'Drop sé' */
@Input('dropZoneEndMsg') _dropZoneEndMsg = 'Drop sé'
/** Disable drop and click - Default = false */
@Input('disabled') _disabled = false
/** Grab the dropped file*/
@Output('fileDrop') _onFileDrop = new EventEmitter<Array<File>>()
/** Grab ALL the files that were dropped SO FAR*/
@Output('newFiles') _onNewFiles = new EventEmitter<Array<File>>()
/** Let user know that something went wrong - eg. wrong file type*/
@Output('error') _onError = new EventEmitter<string>()
_dropZoneMsg: string
files: File[] = []
private _acceptableTypes: string[] = []
//--------------------------------------------//
constructor() { }//ctor
//--------------------------------------------//
ngOnChanges(changes: SimpleChanges): void {
if (changes._accept) {
this._acceptableTypes = this._accept
.split(',')
.map(ac => ac.trim().toLowerCase())
}//if
if (changes._dropZoneStartMsg)
this.initializeDropZoneMessage()
}//ngOnChanges
//--------------------------------------------//
ngOnInit() {
// this.initializeDropZoneMessage()
}//ngOnInit
//--------------------------------------------//
onFileDrop(ev: DragEvent) {
ev.preventDefault();
const newFiles: File[] = []
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
if (ev.dataTransfer.items) {
const items = ev.dataTransfer.items
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < items.length; i++) {
// If dropped items aren't files, reject them
if (items[i].kind !== 'file' || !this.isValidFile(items[i].getAsFile()))
continue
const file = items[i].getAsFile()
newFiles.push(file)
}//For
} else {
const files = ev.dataTransfer.files
// Use DataTransfer interface to access the file(s)
for (let i = 0; i < files.length; i++) {
const file = files[i]
if (!this.isValidFile(file))
continue
newFiles.push(file)
}//For
}//Else
this.emitFiles(newFiles)
// Pass event to removeDragData for cleanup
this.removeDragData(ev)
}//onFileDrop
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
/** Called when drop zone is clicked */
onFileClick(files: FileList) {
if (!this._clickable)
return
const newFiles: File[] = []
for (let i = 0; i < files.length; i++) {
this.files.push(files[i])
newFiles.push(files[i])
}//for
this.emitFiles(newFiles)
}//onFileClick
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
/** Prevent default behavior (Prevent file from being opened) */
onDrag(ev: DragEvent) {
if (this._disabled)
return
this._dropZoneMsg = this._dropZoneEndMsg
ev.preventDefault()
}//dragOverHandler
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
/** Prevent default behavior (Prevent file from being opened) */
onDragEnd(ev: DragEvent) {
this._dropZoneMsg = this._dropZoneStartMsg
}//dragOverHandler
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
removeDragData(ev: DragEvent) {
if (ev.dataTransfer.items)
// Use DataTransferItemList interface to remove the drag data
ev.dataTransfer.items.clear()
else
// Use DataTransfer interface to remove the drag data
ev.dataTransfer.clearData()
}//removeDragData
//--------------------------------------------//
initializeDropZoneMessage() {
this._dropZoneMsg = this._dropZoneStartMsg
}//initializeDropZoneMessage
//--------------------------------------------//
isValidFile(file: File): boolean {
const fileType = file.type.trim().toLowerCase()
console.log(fileType)
const idx = this._acceptableTypes
.findIndex(tp => tp === fileType)
//-1 means it wasn't found
const isValid = idx !== -1
if (!isValid)
this._onError.emit('Incorrect file type!')
return isValid
}//isValidFile
//--------------------------------------------//
emitFiles(newFiles: File[]) {
if (this._disabled)
return
//Anything to emit?
if (!newFiles?.length)
return
if (!this._multiple && newFiles.length > 1)
return
if (this._multiple)
this.files = this.files.concat(newFiles)
else
this.files = newFiles
this._onFileDrop.emit(newFiles)
this._onNewFiles.emit(this.files)
}//emitFiles
//--------------------------------------------//
}//Cls
Then use it like this:
<inigo-drop-zone
[dropZoneStartMsg]="Click or Drag & Drop to add file.'"
[multiple]="false"
(fileDrop)="onFileDrop($event)"
[accept]="_myAcceptableTypes"
(error)="onError($event)">
</inigo-drop-zone>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | ShanieMoonlight |