2

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 ?

1
  • 1
    what is the problem with your code? Commented Mar 24, 2021 at 16:47

1 Answer 1

2

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>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.