Angular 9 File Upload With Progress Bar Using Reactive Forms

0
525
File Upload in Angular 9

File upload in Angular 9 can be quite hard. You need to deal with files in JavaScript and Back end API. In this article, we will learn how to do both. Here i am going to deal with Angular 9 File Upload

For Video Tutorials Subscribe my YouTube Channel

Back End Code

First, we will create a PHP file to handling file uploads. Your PHP file code like the following.

<?php
$name = $_POST['name'];
if($_FILES['file']){
    $path = 'uploads/';
    if (!file_exists($path)) {
        mkdir($path, 0777, true);
    }
    $originalName = $_FILES['file']['name'];
    $ext = '.'.pathinfo($originalName, PATHINFO_EXTENSION);
    $t=time();
    $generatedName = md5($t.$originalName).$ext;
    $filePath = $path.$generatedName;
    if (move_uploaded_file($_FILES['file']['tmp_name'], $filePath)) {
        echo json_encode(array(
            'result' => 'success',
            'status' => true,
        ));
    }
}
?>

Here, we getting two inputs using the POST method.

<?php 
$name = $_POST['name']; //first Input
$_FILES['file'] //file input - second

The following code checks the upload directory already exist. If the directory does not exist. It will create a new directory. We are going to upload all the files into the created directory.

<?php
// ...
//....
$path = 'uploads/';
if (!file_exists($path)) {
mkdir($path, 0777, true);
}

Sometimes the different files have the same name. During file upload, the old file replaced by the new file. The following code reduce this

<?php 
//....
//.....
$originalName = $_FILES['file']['name'];
$ext = '.'.pathinfo($originalName, PATHINFO_EXTENSION);
$t=time();
$generatedName = md5($t.$originalName).$ext;
$filePath = $path.$generatedName;

The uploaded files are stored in a directory somewhere on your server.

File Upload in Angular 9
<?php
//...
//....
move_uploaded_file($_FILES['file']['tmp_name'], $filePath

this code does that work.

Create a New Angular Application

Next, we will move on to Angular Part. We need to create a new angular project. We are going to use the angular-cli for this project. Create a new application by opening a command prompt at the desired location and type:

ng new fileUpload

We need some complex UI-elements such as Form Elements, Progress Bar. I decided to use the Bootstrap for this project. Install the following dependencies using npm.

npm install bootstrap --save
npm install jquery --save
npm install dropify --save

Here I am using dropify. It’s a JQuery plugin for styling the file input field. So I need to install JQuery and dropify.

Import The Script and CSS files

To make the css and js files available in our app, we need to import it into our angular.json file:

"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.css",
  "node_modules/bootstrap/dist/css/bootstrap-reboot.css",
  "node_modules/bootstrap/dist/css/bootstrap-grid.css",
  "src/styles.scss",
  "node_modules/dropify/dist/css/dropify.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.js",
  "node_modules/bootstrap/dist/js/bootstrap.js",
  "node_modules/dropify/dist/js/dropify.js"
]

File Upload Service

Create a new service using angular-cli generators to handle the file upload in front end.

ng g service upload

Related Articles

Ionic 4 Google Map

Angular Data Tables

Import Modules

Import ReactiveFormModule and HttpClientModule into your app module.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {ReactiveFormsModule} from "@angular/forms";
import {HttpClientModule} from "@angular/common/http";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
      ReactiveFormsModule,
      HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Inside of upload service, we need to use the HttpClient, so we request it using dependency injection.
Also, the service will contain only one method called “uploadFile”.

upload.service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";

@Injectable({
  providedIn: 'root'
})
export class UploadService {

  constructor(private http: HttpClient) { }

  uploadFile(formData){
    const url = 'http://localhost/filetest/file.php';
    return this.http.post(url, formData, {
      reportProgress: true,
      observe: 'events'
    });
  }
}

Url used to points the backend API URL

const url = 'http://localhost/filetest/file.php';

the following code is used for getting the file upload status in percentage

{
      reportProgress: true,
      observe: 'events'
}

Inside of app component class, we need to use the FormBuilder, UploadService, DomSanitizer, so we request it using dependency injection.

constructor(private fb: FormBuilder, private upService: UploadService, private _sanitizer: DomSanitizer)

Create Reactive Form

create a new formGruop property for initializing the form.

FileForm: FormGroup;

next, configure the form control elements

this.FileForm = this.fb.group({
  'FileName': ['',  Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(1500)])],
  'File': [''],
});

Create a template reference variable to access the file input field inside of your component class

@ViewChild('File', {static: false}) InputFile;

Create a file property with File annotation for holding file information.

UploadFile: File;

Declare $ as a global variable of the component class for accessing the jquery functionalities

declare var $: any;

Dropify Initialization

On ngOnInit() initialize the dropify plugin.

$(() => {
  $('.dropify').dropify();
});

Angular 9 File Upload With Progress Bar

Create a new method inside of component class, called “upload”. This method holding the code for file capture and upload to the server.

Upload file with progress bar
upload(value){
  const file = this.InputFile.nativeElement;
  if (file.files && file.files[0]) {
    this.UploadFile = file.files[0];
  }
  const formData = new FormData();
  formData.append('name', value.name);
  formData.append('file', this.UploadFile);
  this.upService.uploadFile(formData).subscribe(
      event => {
        if(event.type === HttpEventType.UploadProgress){
          this.UploadProgress = Math.round((event.loaded / event.total) * 100);
          console.log(this.UploadProgress);
        } else if(event.type === HttpEventType.Response) {
          let response: any;
          response = event.body;
          console.log(response);
        }
      }
  );

}

the following code used for getting the file from the input field.

const file = this.InputFile.nativeElement;
if (file.files && file.files[0]) {
  this.UploadFile = file.files[0];
}

Here, I call the uploadFile method from upload services.

this.upService.uploadFile(formData).subscribe(
      event => {
        if(event.type === HttpEventType.UploadProgress){
          this.UploadProgress = Math.round((event.loaded / event.total) * 100);
          console.log(this.UploadProgress);
        } else if(event.type === HttpEventType.Response) {
          let response: any;
          response = event.body;
          console.log(response);
        }
      }
  );

A lot of Http Events available in angular, in this code I am using UploadProgress and Response events. The UploadProgress event for getting file upload percentage and the Response event for the final result.

Finally your component class code like this

import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {FileUploadService} from '../services/file-upload.service';
import {DomSanitizer} from '@angular/platform-browser';
import {HttpEventType} from '@angular/common/http';
declare const $: any;

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.scss']
})
export class FileUploadComponent implements OnInit, AfterViewInit {
  FileForm: FormGroup;
  @ViewChild('File', {static: false}) InputFile;
  UploadFile: File;
  UploadProgress = 0;
  constructor(private fb: FormBuilder, private upService: FileUploadService, private sanitizer: DomSanitizer) {
    this.FileForm = this.fb.group({
      FileName: ['',  Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(1500)])],
      File: [''],
    });
  }

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    $(() => {
      $('.dropify').dropify();
    });
  }

  upload(value){
    const file = this.InputFile.nativeElement;
    if (file.files && file.files[0]) {
      this.UploadFile = file.files[0];
    }
    const formData = new FormData();
    formData.append('name', value.name);
    formData.append('file', this.UploadFile);
    this.upService.uploadFile(formData).subscribe(
      event => {
        if (event.type === HttpEventType.UploadProgress) {
          this.UploadProgress = Math.round((event.loaded / event.total) * 100);
          console.log(this.UploadProgress);
        } else if (event.type === HttpEventType.Response) {
          let response: any;
          response = event.body;
          console.log(response);
        }
      }
    );
  }

  getUploadProgress(value) {
    return this.sanitizer.bypassSecurityTrustStyle('width:' + value + '%');
  }


}

Prepare Your HTML For Angular 9 File Upload

Inside of app component template, we need to create a form and a progress bar.

app.component.html

<div class="container">
  <form [formGroup]="FileForm" (ngSubmit)="upload(FileForm.value)">
    <div class="form-group">
      <label for="email">File name</label>
      <input type="text" class="form-control" id="email" formControlName="FileName">
    </div>
    <div class="form-group">
      <label>File</label>
      <input type="file" class="form-control" class="dropify" formControlName="File" #File>
    </div>
    <button type="submit" class="btn btn-default">Submit</button>
  </form>
  <br>
  <div class="progress">
    <div class="progress-bar progress-bar-success bg-success progress-bar-striped" role="progressbar" attr.aria-valuenow="{{UploadProgress}}" aria-valuemin="0" aria-valuemax="100" [style]="getUploadProgress(UploadProgress)">
      {{UploadProgress}}% Complete (success)
    </div>
  </div>
</div>

Reactive form approach used here.

<form [formGroup]="FileForm" (ngSubmit)="upload(FileForm.value)">

FormGroup aggregates the values of each FormControl child into one object, with each control name as the key.

<input type="text" class="form-control" id="email" formControlName="FileName">

this input field gets the name from the file. formControlName="FileName" Syncs a FormControl in an existing FormGroup to a form control element by name.

<input type="file" class="form-control" class="dropify" formControlName="File" #File>

this is the file input field. Add a template reference #File variable for file input field. we can get the file from using this template reference variable in the component class.

<div class="progress">
    <div class="progress-bar progress-bar-success bg-success progress-bar-striped" role="progressbar" attr.aria-valuenow="{{UploadProgress}}" aria-valuemin="0" aria-valuemax="100" [style]="getUploadProgress(UploadProgress)">
      {{UploadProgress}}% Complete (success)
    </div>
  </div>

this is the code for the bootstrap progress bar.

Happy Coding..

For Video Tutorials Subscribe my YouTube Channel