Angular 16 File Upload with Progress Bar Tutorial

Last Updated on by in Angular Tutorial
Today, through this post. We are going to learn how to upload a file and create a progress bar in an Angular 16 app using HttpClient, HttpEvent, and HttpEventType APIs.A progress bar is a graphical UI representation to visualize the progress of programmatic operation such as uploading, downloading, transfer, and installation of a data.

We will be setting up a basic Angular app with Express API and make the Http POST request to send the user name and image to the server.

The Primary purpose of this tutorial is to learn how to listen to the progress of the HTTP requests, especially when the data is being uploaded to the webserver.

Angular 16 File Upload with Progress Bar Example

  • Create Angular app
  • Build Node Server
  • Set up Angular App
  • Create Angular Service for Angular Progress Bar Demo
  • Build Angular File Upload System with Progress Bar
  • Start Development Server

Create Angular app

Install an Angular app from scratch to show you the file upload and progress bar demo:

ng new fileupload-progressbar-angular

Get inside the project folder:

cd fileupload-progressbar-angular

Install and set up the Bootstrap 5 UI framework.

npm install bootstrap

Inject the bootstrap style-sheet in the styles array in angular.json file.

"styles": [
          "node_modules/bootstrap/dist/css/bootstrap.min.css",
          "src/styles.scss"
         ]

No Property Access From Index Signature

To resolve error:

Property ‘xxxName’ comes from an index signature, so it must be accessed with [‘xxxName’]

This setting makes sure profound consistency between accessing a field via the “dot” (obj.key) syntax, and “indexed” (obj["key"]) and the way which the property is declared in the type.

Without this flag, TypeScript will allow you to use the dot syntax to access fields which are not defined:

Make sure to set noPropertyAccessFromIndexSignature property to false under compilerOptions in tsconfig.json file:

"compilerOptions": {
 ...
 ...
   "strict": false,
   "noPropertyAccessFromIndexSignature": false,
 ...
 ...
}

Build Node Server

We will now set up a node and express server using npm packages. Along with Multer, it lets you store the image files along with other NPM packages.

Create a new “backend” folder:

mkdir backend && cd backend

Create separate package.json for node server.

npm init -y

Run command to install required NPM packages.

npm install body-parser cors express mongoose multer

Install nodemon library and get rid from restarting the server when make changes in backend files.

npm install nodemon --save-dev

Define Mongoose Schema

Create backend/models folder, also create User.js file and paste the given code.

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Define Schema
let userSchema = new Schema(
  {
    _id: mongoose.Schema.Types.ObjectId,
    name: {
      type: String,
    },
    avatar: {
      type: String,
    },
  },
  {
    collection: "users",
  }
);
module.exports = mongoose.model("User", userSchema);

Build File Upload REST API with Multer & Express

Create new backend/public folder, here you can keep all the uploaded files.

Run the command from the backend folder’s root.

mkdir public

Create a backend/routes folder, and create router handling file by the name of user.routes.js.

const express = require("express");
const multer = require("multer");
const mongoose = require("mongoose");
const router = express.Router();
// Multer File upload settings
const DIR = "./public/";
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, DIR);
  },
  filename: (req, file, cb) => {
    const fileName = file.originalname.toLowerCase().split(" ").join("-");
    cb(null, fileName);
  },
});
// Multer Mime Type Validation
var upload = multer({
  storage: storage,
  // limits: {
  //   fileSize: 1024 * 1024 * 5
  // },
  fileFilter: (req, file, cb) => {
    if (
      file.mimetype == "image/png" ||
      file.mimetype == "image/jpg" ||
      file.mimetype == "image/jpeg"
    ) {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error("Only .png, .jpg and .jpeg format allowed!"));
    }
  },
});
// User model
let User = require("../models/User");
// POST User
router.post("/create-user", upload.single("avatar"), (req, res, next) => {
  const url = req.protocol + "://" + req.get("host");
  const user = new User({
    _id: new mongoose.Types.ObjectId(),
    name: req.body.name,
    avatar: url + "/public/" + req.file.filename,
  });
  user
    .save()
    .then((result) => {
      console.log(result);
      res.status(201).json({
        message: "User registered successfully!",
        userCreated: {
          _id: result._id,
          name: result.name,
          avatar: result.avatar,
        },
      });
    })
    .catch((err) => {
      console.log(err),
        res.status(500).json({
          error: err,
        });
    });
});
// GET All Users
router.get("/", (req, res, next) => {
  User.find().then((data) => {
    res.status(200).json({
      message: "Users retrieved successfully!",
      users: data,
    });
  });
});
module.exports = router;

Configure Node/Express Server

Create index.js file inside the backend folder. And, insert the below code within the index.js file.

const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const bodyParser = require("body-parser");
// Routes to Handle Request
const userRoute = require("./routes/user.routes");
// Connecting MongoDB
async function mongoDbConnection() {
  await mongoose.connect(
    "mongodb://127.0.0.1:27017/test",
    {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    },
    6000
  );
}
mongoDbConnection().then(() => {
  console.log("MongoDB successfully connected.");
}),
  (err) => {
    console.log("Could not connected to database : " + err);
  };
// Setup Express.js
const app = express();
app.use(bodyParser.json());
app.use(
  bodyParser.urlencoded({
    extended: false,
  })
);
app.use(cors());
// Make Images "Uploads" Folder Publicly Available
app.use("/public", express.static("public"));
// API Route
app.use("/api", userRoute);
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
  console.log("Connected to port " + port);
});
// Error
app.use((req, res, next) => {
  // Error goes via `next()` method
  setImmediate(() => {
    next(new Error("Something went wrong"));
  });
});
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Start Node Server

Make sure to setup and start the MongoDB community and MongoDB Compass GUI database in your local system.

Also, pass the MongoDB database URL to the mongoose.connect(‘…’) method in the backend/index.js file.

Then, open another terminal and run following command.

npx nodemon server

Next, you can checkout node server running on the following Url: http://localhost:4000/api

API MethodURL
GEThttp://localhost:4000/api
POST/api/create-user

Create Angular Service for Angular Progress Bar Demo

Before creating file upload service, we need to import the HttpClientModule service in app.module.ts file.

import { HttpClientModule } from '@angular/common/http';
@NgModule({
  declarations: [...],
  imports: [
     HttpClientModule
  ],
  bootstrap: [...]
})
export class AppModule { }

In this step, we will create file upload service with HttpClient run the following command to create the service.

ng generate service file-upload

Next, open the src/app/file-upload.service.ts file and add the following code
inside of it.

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { HttpErrorResponse, HttpClient } from '@angular/common/http';
@Injectable({
  providedIn: 'root',
})
export class FileUploadService {
  constructor(private http: HttpClient) {}
  addUser(name: string, profileImage: File): Observable<any> {
    var formData: any = new FormData();
    formData.append('name', name);
    formData.append('avatar', profileImage);
    return this.http
      .post('http://localhost:4000/api/create-user', formData, {
        reportProgress: true,
        observe: 'events',
      })
      .pipe(catchError(this.errorMgmt));
  }
  errorMgmt(error: HttpErrorResponse) {
    let errorMessage = '';
    if (error.error instanceof ErrorEvent) {
      // Get client-side error
      errorMessage = error.error.message;
    } else {
      // Get server-side error
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    console.log(errorMessage);
    return throwError(() => {
      return errorMessage;
    });
  }
}

Build Angular File Upload System with Progress Bar

Create a form which will allow to upload data to the server.

Import the ReactiveFormsModule API in the app.module.ts file.

import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
  declarations: [...],
  imports: [
    ReactiveFormsModule
  ],
  bootstrap: [...]
})
export class AppModule { }

Build a file upload form with Bootstrap form components and Reactive Forms APIs.

Go to app/app.component.html file and add the following code:

<div class="container">
    <form [formGroup]="form" (ngSubmit)="submitUser()">
        <!-- Progress Bar -->
        <div class="progress form-group" *ngIf="progress > 0">
            <div class="progress-bar progress-bar-striped bg-success" role="progressbar" [style.width.%]="progress">
            </div>
        </div>
        <div class="form-group">
            <input type="file" (change)="uploadFile($event)">
        </div>
        <div class="form-group input-group-lg">
            <input class="form-control" placeholder="Name" formControlName="name">
        </div>
        <div class="form-group">
            <button class="btn btn-danger btn-block btn-lg">Create</button>
        </div>
    </form>
</div>

Next, go to app.component.ts file and add the following code inside of it.

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from "@angular/forms";
import { FileUploadService } from "./file-upload.service";
import { HttpEvent, HttpEventType } from '@angular/common/http';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  form: FormGroup;
  progress: number = 0;
  constructor(
    public fb: FormBuilder,
    public fileUploadService: FileUploadService
  ) {
    this.form = this.fb.group({
      name: [''],
      avatar: [null]
    })
  }
  ngOnInit() { }
  uploadFile(event) {
    const file = (event.target as HTMLInputElement).files[0];
    this.form.patchValue({
      avatar: file
    });
    this.form.get('avatar').updateValueAndValidity()
  }
  submitUser() {
    this.fileUploadService.addUser(
      this.form.value.name,
      this.form.value.avatar
    ).subscribe((event: HttpEvent<any>) => {
      switch (event.type) {
        case HttpEventType.Sent:
          console.log('Request has been made!');
          break;
        case HttpEventType.ResponseHeader:
          console.log('Response header has been received!');
          break;
        case HttpEventType.UploadProgress:
          this.progress = Math.round(event.loaded / event.total * 100);
          console.log(`Uploaded! ${this.progress}%`);
          break;
        case HttpEventType.Response:
          console.log('User successfully created!', event.body);
          setTimeout(() => {
            this.progress = 0;
          }, 1500);
      }
    })
  }
}

Run the command to start your Angular File upload with progress bar app.

ng serve --open

Angular Progress Bar

Conclusion

Finally, Angular file upload with progress bar tutorial is completed. I hope you enjoyed this tutorial and surely share with coders community.

Use the below link to obtain the complete code of this guide.

Git Repo

❤️ Checkout Popular CSS Collection

Digamber - Author positronX.io

Digamber, the founder of PositronX, is a certified web developer with over 10 years of experience. He possesses a curiosity for learning new things and enjoys playing cricket on his days off.