How to upload Angular 8 & Express files with Reactive forms

This is image title
We will create a basic Angular app and set up a Node.js backend using Node, Express.js, and MongoDB. Then, we will take the help of Multer NPM module to upload and store the files in Node server.

Install & Set up Angular Project

Run following command to install basic Angular project:

ng new mean-stack-file-upload

# ? Would you like to add Angular routing? Yes
# ? Which stylesheet format would you like to use? CSS

Get inside the project folder:

cd mean-stack-file-upload

We will also install Bootstrap 4 by running the following command.

npm install bootstrap

Go to angular.json file and inject the bootstrap style sheet inside the styles array like given below.

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

Run given below commands from your terminal to create Angular components for managing file uploading task in a MEAN stack app.

ng g c create-user

ng g c users-list

Now, your basic Angular project is ready to be served, run the below command to start the Angular app.

ng serve --open

Enable Angular Routing

Let’s enable routing in our Angular app, go to app-routing.module.ts file and add the following code inside of it.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { Routes, RouterModule } from '@angular/router';
import { CreateUserComponent } from './create-user/create-user.component';
import { UsersListComponent } from './users-list/users-list.component';

const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'add-user' },
  { path: 'add-user', component: CreateUserComponent },
  { path: 'users-list', component: UsersListComponent }
]

@NgModule({
  declarations: [],
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

export class AppRoutingModule { }

Now, visit app.component.html file and add the following code to enable routing service in our Angular file uploading demo app.

<ul>
  <li>
    <a routerLinkActive="active" routerLink="/add-user">Create User</a>
  </li>
  <li>
    <a routerLinkActive="active" routerLink="/users-list">Users</a>
  </li>
</ul>

<router-outlet></router-outlet>

This is image title

Set up Node Server

We will set up a separate node server for managing image uploading in our Angular application. Create a new folder in the root of our Angular application, name it backend.

Run the following command from the root of your Angular app to generate backend folder:

mkdir backend && cd backend

We will be using separate package.json file to manage our node server.

npm init

Install required dependencies to build node and express file uploading server:

npm install body-parser cors express mongoose multer --save

Then, install nodemon NPM module, it automatically restarts the node application when it detects the change in the server files.

npm install nodemon --save-dev

Set up MongoDB Database

Next, we will set up a mongoDB database connection. In mongoDB NoSQL database, we will store our files or images.

Create a folder in backend folder and name it database also create a file and name it db.js. Here we will define mongoDB database configuration.

Go to backend/database/db.js file and place the following code in it.

module.exports = {
  db: 'mongodb://localhost:27017/meanfileupload'
}

Set up Mongoose Schema
In the next step, we will declare the Mongoose Schema for our Angular 8 MEAN stack file uploading tutorial.

Create a folder name it models inside the backend folder. Then creates a file and name it User.js and include the following code in it.

const mongoose = require('mongoose');
const Schema = mongoose.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 Express Routes for File Uploading using Multer

Now we will build Express REST API routes for file uploading using Multer. Create a new folder inside backend folder and name it routes, inside this folder also create a new file and name it user.route.js.

Create a new folder by the name of public in the backend folder. When a user makes the HTTP POST request via Express.js route from Angular service then in this folder an image will be stored.

Go to backend/routes/user.route.js file and add the following code.

let express = require('express'),
  multer = require('multer'),
  mongoose = require('mongoose'),
  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 User
router.get("/", (req, res, next) => {
  User.find().then(data => {
    res.status(200).json({
      message: "Users retrieved successfully!",
      users: data
    });
  });
});


// GET User
router.get("/:id", (req, res, next) => {
  User.findById(req.params.id).then(data => {
    if (data) {
      res.status(200).json(post);
    } else {
      res.status(404).json({
        message: "User not found!"
      });
    }
  });
});


module.exports = router;
  • We imported the express, multer, and mongoose NPM modules to make the REST APIs routes.

  • Declare the Dir variable and define the public directory path, where all the images or files will be stored.

  • We are using multer disktorage middleware. In this method, we used destination and filename methods. Multer’s destination method stores files in the public folder. The filename method takes req, file, cb arguments, and helps in defining the name of the file.

  • Setting up file upload limit and file type validation is easy by using Multer NPM Module. In the above example, we used limits key to defining fileSize, and file upload limit is upto 5mb.

  • Multer’s fileFilter method allows MIME-type validation, we implemented specific file type validation in which we can upload images with particular file types such as .png, .jpg, and .jpeg format.

  • We created the express route by the name of /create-user, this middleware takes req, res, next arguments. We can define the Multer’s upload object directly with the express route. Whenever this API is being called, then the file will be saved in the public directory.

Final Node Server Configuration

Next, we will create server.js file in the backend folder’s root. Here we will define the server configurations such as mongoDB database, Express routes, Express server setup, Express Static Path, Server PORT and Error handling methods:

Go to backend/server.js file and add the following code inside of it.

let express = require('express'),
  mongoose = require('mongoose'),
  cors = require('cors'),
  bodyParser = require('body-parser'),
  dbConfig = require('./database/db');


// Routes to Handle Request
const userRoute = require('../backend/routes/user.route')


// MongoDB Setup
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db, {
  useNewUrlParser: true
}).then(() => {
  console.log('Database sucessfully connected')
},
  error => {
    console.log('Database could not be connected: ' + error)
  }
)


// Setup Express.js
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: false
}));
app.use(cors());


// Make "public" Folder Publicly Available
app.use('/public', express.static('public'));


// API Route
app.use('/api', userRoute)


// Error favicon.ico
app.get('/favicon.ico', (req, res) => res.status(204));


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);
});

Note : We used the express.static() method. This method is essential and makes a public folder to publicly available. So when we access files from Angular’s frontend, then we can access these files easily.

This is image title

Start the MEAN Stack Server

  • cd backend to enter into the backend folder
  • nodemon server to start the nodemon server
  • mongod to start the mongoDB shell
    You can check out uploaded data on the following URL: http://localhost:4000/api

Create Angular File Uploading Service

In this step, we will create an Angular 8 service to handle node server REST APIs for our file upload tutorial.

But before that create a folder and name it shared inside Angular’s src/app folder.

Inside the src/app/shared folder create user.ts class, and define the following code inside of it.

export class User {
    id: string;
    name: string;
    avatar: string;
}

Next, we will import the HttpClientModule service in app.module.ts file:

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [...],
  imports: [
     HttpClientModule
  ],
  bootstrap: [...]
})

export class AppModule { }

Then, go to src/app/shared folder and create file-upload.service.ts file, and place the given below code in it.

import { Injectable } from '@angular/core';
import { User } from './user';
import { Observable, throwError } from 'rxjs';
import { HttpHeaders, HttpErrorResponse, HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class FileUploadService {

  baseURL = "http://localhost:4000/api";
  headers = new HttpHeaders().set('Content-Type', 'application/json');

  constructor(private http: HttpClient) { }

  // Get Users
  getUsers() {
    return this.http.get(this.baseURL)
  }

  // Create User
  addUser(name: string, profileImage: File): Observable<any> {
    var formData: any = new FormData();
    formData.append("name", name);
    formData.append("avatar", profileImage);

    return this.http.post<User>(`${this.baseURL}/create-user`, formData, {
      reportProgress: true,
      observe: 'events'
    })
  }

  // Error handling 
  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(errorMessage);
  }

}

We created the Angular service for the file uploading task. In this service file, we defined the methods such as getUsers to retrieve the user’s data from the mongoDB database and addUser method to upload the user data such as name and profile image to the mongoDB database. In order to use this service we have to import this service and inject inside the component’s constructor method in Angular’s component.

To upload the file or image in the mongoDB database via node server, we are using the FormData object. The FormData interface provides a way to easily construct a set of key/value pairs describing form fields and their values. We passed the name and profileImage as an argument. Then we declared the FormData object and created a formData instance from it. After that, we used the formData.append() method to inject the values retrieved from the Reactive form.

Next, we are using the Http POST method to send the user data to the server. We passed the two arguments in the POST method; first, we passed the REST API route, and the second argument is the fromData created with FormData object. We also defined reportProgress: true and observe: ‘events’ value because we want to track the Http request’s progress.

Angular 8 File Upload System with Reactive Forms

In this segment, we will learn to create an Angular 8 file uploading system with Reactive Forms.

Go to app.module.ts file and import the ReactiveFormsModule service.

import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [...],
  imports: [
    ReactiveFormsModule
  ],
  bootstrap: [...]
})

export class AppModule { }

Go to src/app/create-user.component.ts file and add the following code.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from "@angular/forms";
import { FileUploadService } from "../shared/file-upload.service";
import { HttpEvent, HttpEventType } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-create-user',
  templateUrl: './create-user.component.html',
  styleUrls: ['./create-user.component.css']
})

export class CreateUserComponent implements OnInit {
  preview: string;
  form: FormGroup;
  percentDone: any = 0;
  users = [];

  constructor(
    public fb: FormBuilder,
    public router: Router,
    public fileUploadService: FileUploadService
  ) {
    // Reactive Form
    this.form = this.fb.group({
      name: [''],
      avatar: [null]
    })
  }

  ngOnInit() { }

  // Image Preview
  uploadFile(event) {
    const file = (event.target as HTMLInputElement).files[0];
    this.form.patchValue({
      avatar: file
    });
    this.form.get('avatar').updateValueAndValidity()

    // File Preview
    const reader = new FileReader();
    reader.onload = () => {
      this.preview = reader.result as string;
    }
    reader.readAsDataURL(file)
  }

  submitForm() {
    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.percentDone = Math.round(event.loaded / event.total * 100);
          console.log(`Uploaded! ${this.percentDone}%`);
          break;
        case HttpEventType.Response:
          console.log('User successfully created!', event.body);
          this.percentDone = false;
          this.router.navigate(['users-list'])
      }
    })
  }

}

Next, go to src/app/create-user.component.html file and add the following code.

<form [formGroup]="form" (ngSubmit)="submitForm()">
  <!-- Progress Bar -->
  <div class="progress form-group" *ngIf="fileUploadService.percentDone">
    <div class="progress-bar progress-bar-striped bg-success" role="progressbar"
      [style.width.%]="fileUploadService.percentDone">
    </div>
  </div>

  <!-- Image Preview -->
  <div class="form-group">
    <div class="preview" *ngIf="preview && preview !== null">
      <img [src]="preview" [alt]="form.value.name">
    </div>
  </div>

  <!-- File Input -->
  <div class="form-group">
    <input type="file" (change)="uploadFile($event)">
  </div>

  <!-- Name -->
  <div class="form-group input-group-lg">
    <input class="form-control" placeholder="Name" formControlName="name">
  </div>

  <!-- Submit -->
  <div class="form-group">
    <button class="btn btn-danger btn-block btn-lg">Create User</button>
  </div>
</form>
  • We used created the basic form using Bootstrap 4 UI components.

  • We are using Reactive Forms to manage the data.

  • To show image preview in Angular, we declared the uploadFile method and using the FileReader method to create the reader instance. The reader instance will use the readAsDataURL method and convert the base64 image to show the image preview. You can check out this detailed article on Angular image preview with Reactive Forms.

  • Next, access the addUser method from Angular service. This method will take name and avatar values to store the data on the MongoDB database. When we subscribe to this method, then it will also track the File or data upload with progress bar using HttpEvent and HttpEventType services

Show User Data List

Next, we will show user uploaded data on Angular’s frontend, go to users-list/users-list.component.ts file and add the following code inside of it.

import { Component, OnInit } from '@angular/core';
import { FileUploadService } from "../shared/file-upload.service";

@Component({
  selector: 'app-users-list',
  templateUrl: './users-list.component.html',
  styleUrls: ['./users-list.component.css']
})

export class UsersListComponent implements OnInit {

  Users: any = [];

  constructor(public fileUploadService: FileUploadService) {
    this.getUsers();
  }

  ngOnInit() { }

  getUsers() {
    this.fileUploadService.getUsers().subscribe((res) => {
      this.Users = res['users'];
    })
  }

}

Then, go to users-list/users-list.component.html file and include the given below
code inside of it.

<div class="container user-table">

    <!-- No data message -->
    <div class="alert alert-success text-center" role="alert" *ngIf="Users.length <= 0">
        No Users added yet!
    </div>

    <ul class="list-unstyled">
        <li class="media" *ngFor="let user of Users; let i = index">
            <img [src]="user.avatar" class="mr-3" [alt]="user.name">
            <div class="media-body">
                <h5 class="mt-0 mb-1">{{user.name}}</h5>
                {{user._id}}
            </div>
        </li>
    </ul>

</div>

Conclusion

Finally, we have completed Angular 8 and Express JS File Upload with Reactive Forms tutorial. In this tutorial, we learnt to upload files from Angular app to mongoDB database using node and express server. We learned to show image preview and making image or file upload progress bar using HttpEvent progress API service. We explored about Multer NPM module, and it’s middleware. I hope you enjoyed this article, please consider it sharing with others.

Let’s share it!
This is image title

#angular #javascript #reactjs

How to upload Angular 8 & Express files with Reactive forms
5 Likes30.40 GEEK