Angular MEAN Stack CRUD Tutorial Example

1 0
Read Time:17 Minute, 53 Second

In this MEAN stack Article, I will share step by step process to build an Angular CRUD (Create, Read, Update, Delete) app from scratch.

Let us understand what does MEAN stack means.

  • Mongo DB – It’s an open-source NoSQL cross-platform document-oriented database.
  • Express JS – It’s a web-based application framework work with Node JS, It helps to build web apps and RESTful APIs.
  • Angular – Its a TypeScript based complete front-end framework developed by Google team.
  • Node JS – It is a free JavaScript run time environment, It executes JavaScript code outside of a browser. It is available for MacOS, Windows, Linux and Unix.

Setup Node JS development environment

Follow this link to set up Node JS in your system.

Set up Angular MEAN Stack Project

Install Angular CLI
Angular project is developed using Angular CLI, so before setting up Angular project. You must have Angular CLI installed in your system. Hit the given below command to install the Angular CLI, ignore if Angular CLI is already installed.

npm install @angular/cli -g

Let us install Angular project, run the following command.

ng new mean-stack-crud-app

Angular CLI asks for your choices while setting up the project…

Would you like to add Angular routing?
Select y and Hit Enter.

Which stylesheet format would you like to use? (Use arrow keys)
Choose CSS and hit Enter

Your Angular project is installed now get into the project directory.

cd mean-stack-crud-app

If using visual studio code editor then use the below cmd to open the project.

mean-stack-crud-app>code .

Generate components in Angular app.

In order to manage components i will keep all the components in components folder, use the below cmd to generate components.

ng g c components/employee-create

ng g c components/employee-edit

ng g c components/employee-list

Your Angular app has been set up for MEAN Stack development. enter the below command to run the project.

mean-stack-crud-app>ng serve

Build a Node.JS Backend

To write the manageable code, we should keep the MEAN Stack backend folder separate. Create a folder by the name of the backend in Angular’s root directory. This folder will handle the backend code of our application, remember it will have the separate node_modules folder from Angular.

mean-stack-crud-app>mkdir backend

Enter the below command to get into the backend folder.

mean-stack-crud-app>cd backend

Now you are inside the backend folder, run the below command to create the package.json file. This file will have the meta data of your MEAN Stack app, It is also known as the manifest file of any NodeJS project.

mean-stack-crud-app/backend>npm init -y

– Install and Configure required NPM packages for MEAN Stack app development

Use the below command to install the following node modules.

mean-stack-crud-app/backend>npm install --save body-parser cors express mongoose
  • body-parser: The body-parser npm module is a JSON parsing middleware. It helps to parse the JSON data, plain text or a whole object.
  • CORS: This is a Node JS package, also known as the express js middleware. It allows enabling CORS with multiple options. It is available through the npm registry.
  • Express.js: Express js is a free open source Node js web application framework. It helps in creating web applications and RESTful APIs.
  • Mongoose: Mongoose is a MongoDB ODM for Node. It allows you to interact with MongoDB database.

Starting a server every time a change is made is a time-consuming task. To get rid of this problem we use nodemon npm module. This package restarts the server automatically every time we make a change. We’ll be installing it locally by using given below command.

mean-stack-crud-app/backend>npm install nodemon --save-dev

Now, go within the backend folder’s root, create a file by the name of server.js.
Since, the default server file is index.js therefore make sure to set “main”: “server.js” within package.json file.

Now within the backend > server.js file add the given below code.

const express = require('express')
const path = require('path')
const mongoose = require('mongoose')
const cors = require('cors')
const bodyParser = require('body-parser')

// Connecting with mongo db
mongoose
  .connect('mongodb://127.0.0.1:27017/mydatabase')
  .then((x) => {
    console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)
  })
  .catch((err) => {
    console.error('Error connecting to mongo', err.reason)
  })

// Setting up port with express js
const employeeRoute = require('../backend/routes/employee.route')
const app = express()
app.use(bodyParser.json())
app.use(
  bodyParser.urlencoded({
    extended: false,
  }),
)
app.use(cors())
app.use(express.static(path.join(__dirname, 'dist/mean-stack-crud-app')))
app.use('/', express.static(path.join(__dirname, 'dist/mean-stack-crud-app')))
app.use('/api', employeeRoute)

// Create port
const port = process.env.PORT || 4000
const server = app.listen(port, () => {
  console.log('Connected to port ' + port)
})

// Find 404 and hand over to error handler
app.use((req, res, next) => {
  next(createError(404))
})

// error handler
app.use(function (err, req, res, next) {
  console.error(err.message) // Log error message in our server's console
  if (!err.statusCode) err.statusCode = 500 // If err has no specified error code, set error code to 'Internal Server Error (500)'
  res.status(err.statusCode).send(err.message) // All HTTP requests must have a response, so let's send back an error with its status code and message
})

Create Model with Mongoose JS

mkdir models
cd models

Then we will create the Employee.js file.

In this file i will define the Schema for employees collection. My data types are name, email, designation and phoneNumber. Add the given below code in
backend > models > Employee.js file.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// Define collection and schema
let Employee = new Schema({
   name: {
      type: String
   },
   email: {
      type: String
   },
   designation: {
      type: String
   },
   phoneNumber: {
      type: Number
   }
}, {
   collection: 'employees'
})

module.exports = mongoose.model('Employee', Employee)

Create RESTful APIs using Express JS Routes

Let us create the routes in Angular app to access the Employee data through RESTful APIs. I will be using Mongoose.js in our MEAN Stack Tutorial to create, read, update & delete data from MongoDB database.

Create backend > routes > employee.route.js file inside the routes folder.
Add the given below code to create RESTful APIs in MEAN Stack app using mongoose.js.

const express = require('express');
const app = express();
const employeeRoute = express.Router();

// Employee model
let Employee = require('../models/Employee');

// Add Employee
employeeRoute.route('/create').post((req, res, next) => {
  Employee.create(req.body, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
});

// Get All Employees
employeeRoute.route('/').get((req, res) => {
  Employee.find((error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
})

// Get single employee
employeeRoute.route('/read/:id').get((req, res) => {
  Employee.findById(req.params.id, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
})


// Update employee
employeeRoute.route('/update/:id').put((req, res, next) => {
  Employee.findByIdAndUpdate(req.params.id, {
    $set: req.body
  }, (error, data) => {
    if (error) {
      return next(error);
      console.log(error)
    } else {
      res.json(data)
      console.log('Data updated successfully')
    }
  })
})

// Delete employee
employeeRoute.route('/delete/:id').delete((req, res, next) => {
  Employee.findOneAndRemove(req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({
        msg: data
      })
    }
  })
})

module.exports = employeeRoute;

We have set up our MEAN Stack Angular app’s backend using Node js, Express js, Angular and MongoDB.

We have to start the backend server using given command, each command will be executed in separate terminal window.

Start Nodemon Server

In order to start nodemon server, first enter into the backend folder using given below command.

cd backend

Then run the following command to start the nodemon server.

nodemon server

Start MongoDB Server

Open the new terminal enter into the backend folder then use the below command to start the mongoDB server.

mongod

You can access your API route on given below url, here you can check your data.

Check your Angular frontend on – http://localhost:4200

You can check your api url on – http://localhost:4000/api

MEAN Stack App RESTful APIs

We have successfully created APIs to handle CRUD operations in our MEAN Stack app.

MethodAPI url
GET/api
POST/create
GET/read/id
PUT/update/id
DELETE/delete/id

Activate Routing Service in MEAN Stack Angular App

In order to navigate between multiple components, we must set up routing service in our app.

Now if you remember while setting up an Angular project, CLI asked this question “Would you like to add Angular routing?”. We selected yes, it automatically created app-routing.module.tsand registered in src > app > app.module.ts file.

Include the below code in app-routing.module.ts file to enable routing service in Angular app.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { EmployeeCreateComponent } from './components/employee-create/employee-create.component';
import { EmployeeListComponent } from './components/employee-list/employee-list.component';
import { EmployeeEditComponent } from './components/employee-edit/employee-edit.component';

const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'create-employee' },
  { path: 'create-employee', component: EmployeeCreateComponent },
  { path: 'edit-employee/:id', component: EmployeeEditComponent },
  { path: 'employees-list', component: EmployeeListComponent }  
];

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

export class AppRoutingModule { }

To enable routing service include the below code in app.component.html file.

<nav>
  <a routerLinkActive="active" routerLink="/employees-list">View Employees</a>
  <a routerLinkActive="active" routerLink="/create-employee">Add Employee</a>
</nav>

<router-outlet></router-outlet>

Create Angular Service to Consume RESTful APIs

To consume RESTful API in MEAN Stack Angualr 7 app, we need to create a service file. This service file will handle Create, Read, Update and Delete operations.

Before we create service in MEAN Stack app to consume RESTful APIs, We need to do 2 following things:

Add Forms and HTTP Modules

We need to import HttpClientModule ReactiveFormsModule and FormsModule service in app.module.ts file.

import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    ReactiveFormsModule,
    FormsModule,
    HttpClientModule,
   ]
})

Create Employee Model File

Create src > model > employee.ts file.

ng g cl model/Employee

Add the following code in it.

export class Employee {
   name: string;
   email: string;
   designation: string;
   phoneNumber: number;
}

Create Angular Service

Use the given below cmd to create Angular Service file to manage CRUD operations in MEAN Stack Angular app.

ng g s service/api

Now go to src > app > service > api.service.ts file and add the below code.

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import {
  HttpClient,
  HttpHeaders,
  HttpErrorResponse,
} from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  baseUri: string = 'http://localhost:4000/api';
  headers = new HttpHeaders().set('Content-Type', 'application/json');

  constructor(private http: HttpClient) {}

  // Create
  createEmployee(data): Observable<any> {
    let url = `${this.baseUri}/create`;
    return this.http.post(url, data).pipe(catchError(this.errorMgmt));
  }

  // Get all employees
  getEmployees() {
    return this.http.get(`${this.baseUri}`);
  }

  // Get employee
  getEmployee(id): Observable<any> {
    let url = `${this.baseUri}/read/${id}`;
    return this.http.get(url, { headers: this.headers }).pipe(
      map((res: Response) => {
        return res || {};
      }),
      catchError(this.errorMgmt)
    );
  }

  // Update employee
  updateEmployee(id, data): Observable<any> {
    let url = `${this.baseUri}/update/${id}`;
    return this.http
      .put(url, data, { headers: this.headers })
      .pipe(catchError(this.errorMgmt));
  }

  // Delete employee
  deleteEmployee(id): Observable<any> {
    let url = `${this.baseUri}/delete/${id}`;
    return this.http
      .delete(url, { headers: this.headers })
      .pipe(catchError(this.errorMgmt));
  }

  // 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(() => {
      return errorMessage;
    });
  }
}

We have created Angular service file to handle CRUD operations in our app, now go to app.module.ts file and import this service and add into the providers array like given below.

import { ApiService } from './service/api.service';

@NgModule({
  providers: [ApiService]
})

Register an Employee by Consuming RESTful API in Angular MEAN Stack App

To register an employee we will use Angular service and RESTful APIs. I’ve used Reactive Forms to register an employee. We are also covering Reactive forms validations in our MEAN Stack app tutorial.

Go to components > employee-create > employee-create.component.ts file and add the following code.

import { Router } from '@angular/router';
import { ApiService } from './../../service/api.service';
import { Component, OnInit, NgZone } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

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

export class EmployeeCreateComponent implements OnInit {
  submitted = false;
  employeeForm: FormGroup;
  EmployeeProfile: any = ['Finance', 'BDM', 'HR', 'Sales', 'Admin'];

  constructor(
    public fb: FormBuilder,
    private router: Router,
    private ngZone: NgZone,
    private apiService: ApiService
  ) {
    this.mainForm();
  }

  ngOnInit() {}

  mainForm() {
    this.employeeForm = this.fb.group({
      name: ['', [Validators.required]],
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$'),
        ],
      ],
      designation: ['', [Validators.required]],
      phoneNumber: ['', [Validators.required, Validators.pattern('^[0-9]+$')]],
    });
  }

  // Choose designation with select dropdown
  updateProfile(e) {
    this.employeeForm.get('designation').setValue(e, {
      onlySelf: true,
    });
  }

  // Getter to access form control
  get myForm() {
    return this.employeeForm.controls;
  }

  onSubmit() {
    this.submitted = true;
    if (!this.employeeForm.valid) {
      return false;
    } else {
      return this.apiService.createEmployee(this.employeeForm.value).subscribe({
        complete: () => {
          console.log('Employee successfully created!'),
            this.ngZone.run(() => this.router.navigateByUrl('/employees-list'));
        },
        error: (e) => {
          console.log(e);
        },
      });
    }
  }
}

Go to employee-create.component.html add the following code.

<div class="row justify-content-center">
  <div class="col-md-4 register-employee">
    <!-- form card register -->
    <div class="card-body">
      <form [formGroup]="employeeForm" (ngSubmit)="onSubmit()">
        <div class="form-group">
          <label for="inputName">Name</label>
          <input class="form-control" type="text" formControlName="name" />
          <!-- error -->
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['name'].errors?.['required']"
          >
            Name is required.
          </div>
        </div>

        <div class="form-group">
          <label for="inputEmail3">Email</label>
          <input class="form-control" type="text" formControlName="email" />
          <!-- error -->
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['email'].errors?.['required']"
          >
            Enter your email.
          </div>
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['email'].errors?.['pattern']"
          >
            Enter valid email.
          </div>
        </div>

        <div class="form-group">
          <label for="inputPassword3">Designation</label>
          <select
            class="custom-select form-control"
            (change)="updateProfile($event.target.value)"
            formControlName="designation"
          >
            <option value="">Choose...</option>
            <option
              *ngFor="let employeeProfile of EmployeeProfile"
              value="{{ employeeProfile }}"
            >
              {{ employeeProfile }}
            </option>
          </select>
          <!-- error -->
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['designation'].errors?.['required']"
          >
            Choose designation.
          </div>
        </div>

        <div class="form-group">
          <label for="inputVerify3">Mobile No</label>
          <input
            class="form-control"
            type="text"
            formControlName="phoneNumber"
          />
          <!-- error -->
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['phoneNumber'].errors?.['required']"
          >
            Enter your phone number.
          </div>
          <div
            class="invalid-feedback"
            *ngIf="submitted && myForm['phoneNumber'].errors?.['pattern']"
          >
            Enter Numbers Only
          </div>
        </div>

        <div class="form-group">
          <button class="btn btn-success btn-lg btn-block" type="submit">
            Register
          </button>
        </div>
      </form>
    </div>
  </div>
  <!-- form card register -->
</div>

Show List and Delete

I will show the Employees list using RESTful APIs and Angular service. Go to employee-list/employee-list.component.ts file and include the below code.

import { Component, OnInit } from '@angular/core';
import { ApiService } from './../../service/api.service';

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

export class EmployeeListComponent implements OnInit {
  
  Employee:any = [];

  constructor(private apiService: ApiService) { 
    this.readEmployee();
  }

  ngOnInit() {}

  readEmployee(){
    this.apiService.getEmployees().subscribe((data) => {
     this.Employee = data;
    })    
  }

  removeEmployee(employee, index) {
    if(window.confirm('Are you sure?')) {
        this.apiService.deleteEmployee(employee._id).subscribe((data) => {
          this.Employee.splice(index, 1);
        }
      )    
    }
  }

}

To display employees list open the employee-list/employee-list.component.html file and add the following code in it.

<div class="container">
  <!-- No data message -->
  <p *ngIf="Employee.length <= 0" class="no-data text-center">There is no employee added yet!</p>

  <!-- Employee list -->
  <table class="table table-bordered" *ngIf="Employee.length > 0">
    <thead class="table-success">
      <tr>
        <th scope="col">Employee ID</th>
        <th scope="col">Name</th>
        <th scope="col">Email</th>
        <th scope="col">Designation</th>
        <th scope="col">Phone No</th>
        <th scope="col center">Update</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let employee of Employee; let i = index">
        <th scope="row">{{employee._id}}</th>
        <td>{{employee.name}}</td>
        <td>{{employee.email}}</td>
        <td>{{employee.designation}}</td>
        <td>{{employee.phoneNumber}}</td>
        <td class="text-center edit-block">
          <span class="edit" [routerLink]="['/edit-employee/', employee._id]">
            <button type="button" class="btn btn-success btn-sm">Edit</button>
          </span>
          <span class="delete" (click)="removeEmployee(employee, i)">
            <button type="button" class="btn btn-danger btn-sm">Delete</button>
          </span>
        </td>
      </tr>
    </tbody>
  </table>
</div>

Edit Employees Data

In order to edit employees data we need to add the following code in employee-edit/employee-edit.component.html file.

<div class="row justify-content-center">
  <div class="col-md-4 register-employee">
    <!-- form card register -->
    <div class="card card-outline-secondary">
      <div class="card-header">
        <h3 class="mb-0">Edit Employee</h3>
      </div>
      <div class="card-body">
        <form [formGroup]="editForm" (ngSubmit)="onSubmit()">
          <div class="form-group">
            <label for="inputName">Name</label>
            <input class="form-control" type="text" formControlName="name" />
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['name'].errors?.['required']"
            >
              Name is required.
            </div>
          </div>
          <div class="form-group">
            <label for="inputEmail3">Email</label>
            <input class="form-control" type="text" formControlName="email" />
            <!-- error -->
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['email'].errors?.['required']"
            >
              Enter your email.
            </div>
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['email'].errors?.['pattern']"
            >
              Enter valid email.
            </div>
          </div>

          <div class="form-group">
            <label for="inputPassword3">Designation</label>
            <select
              class="custom-select form-control"
              (change)="updateProfile($event.target.value)"
              formControlName="designation"
            >
              <option value="">Choose...</option>
              <option
                *ngFor="let employeeProfile of EmployeeProfile"
                value="{{ employeeProfile }}"
              >
                {{ employeeProfile }}
              </option>
            </select>
            <!-- error -->
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['designation'].errors?.['required']"
            >
              Choose designation.
            </div>
          </div>

          <div class="form-group">
            <label for="inputVerify3">Mobile No</label>
            <input
              class="form-control"
              type="text"
              formControlName="phoneNumber"
            />
            <!-- error -->
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['phoneNumber'].errors?.['required']"
            >
              Enter your phone number.
            </div>
            <div
              class="invalid-feedback"
              *ngIf="submitted && myForm['phoneNumber'].errors?.['pattern']"
            >
              Enter Numbers Only
            </div>
          </div>

          <div class="form-group">
            <button class="btn btn-success btn-lg btn-block" type="submit">
              Update
            </button>
          </div>
        </form>
      </div>
    </div>
    <!-- form  -->
  </div>
</div>

To edit employees data we need to add the following code in employee-edit/employee-edit.component.ts file.

import { Employee } from './../../model/employee';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from './../../service/api.service';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

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

export class EmployeeEditComponent implements OnInit {
  submitted = false;
  editForm: FormGroup;
  employeeData: Employee[];
  EmployeeProfile: any = ['Finance', 'BDM', 'HR', 'Sales', 'Admin'];

  constructor(
    public fb: FormBuilder,
    private actRoute: ActivatedRoute,
    private apiService: ApiService,
    private router: Router
  ) {}

  ngOnInit() {
    this.updateEmployee();
    let id = this.actRoute.snapshot.paramMap.get('id');
    this.getEmployee(id);
    this.editForm = this.fb.group({
      name: ['', [Validators.required]],
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$'),
        ],
      ],
      designation: ['', [Validators.required]],
      phoneNumber: ['', [Validators.required, Validators.pattern('^[0-9]+$')]],
    });
  }

  // Choose options with select-dropdown
  updateProfile(e) {
    this.editForm.get('designation').setValue(e, {
      onlySelf: true,
    });
  }

  // Getter to access form control
  get myForm() {
    return this.editForm.controls;
  }

  getEmployee(id) {
    this.apiService.getEmployee(id).subscribe((data) => {
      this.editForm.setValue({
        name: data['name'],
        email: data['email'],
        designation: data['designation'],
        phoneNumber: data['phoneNumber'],
      });
    });
  }

  updateEmployee() {
    this.editForm = this.fb.group({
      name: ['', [Validators.required]],
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$'),
        ],
      ],
      designation: ['', [Validators.required]],
      phoneNumber: ['', [Validators.required, Validators.pattern('^[0-9]+$')]],
    });
  }

  onSubmit() {
    this.submitted = true;
    if (!this.editForm.valid) {
      return false;
    } else {
      if (window.confirm('Are you sure?')) {
        let id = this.actRoute.snapshot.paramMap.get('id');
        this.apiService.updateEmployee(id, this.editForm.value).subscribe({
          complete: () => {
            this.router.navigateByUrl('/employees-list');
            console.log('Content updated successfully!');
          },
          error: (e) => {
            console.log(e);
          },
        });
      }
    }
  }
}

We have created basic MEAN Stack Angular CRUD app, now enter the below command to start your project on the browser.

ng serve
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

Leave a Comment