webbuild infotech

crud-angular

CRUD operation in Angular, Express JS and MongoDB (MEAN)

CRUD operation in Angular, Express JS, and MongoDB (MEAN)

In this blog, we are going to learn the CRUD operation with Angular, Express Js, and MongoDB, It’s called the MEAN stake so Let’s understand the folder structure first.

Angular-Crud

So here you can see the src, e2e, package.json, angular.json, backend, etc… folders where all files and folders of angular build in folders except the backend folder.  Here backend folder is created in express js and MongoDB where make the API.

All packages are installed in package.json for Angular and Express JS as well.

Here the thing is if press the npm start command then check the package.json file and make the build of angular and that build folder generate inside of the backend/build folder so we can run the single backend only and on the same path we can call the UI and API both.

 

So let’s discuss more with the steps.

Step 1:  Create the angular project with ng new project-name and this command will create an angular folder with some packages and files.

Step 2: Create a backend folder with express backend command inside of the above angular folder that was created with the above step. so this backend folder is used for API and database connection.

 

Step 3: create an ENV file inside the backend folder and make connection URL and PORT number

# .env
DATABSE_CONNECTION = databse_connection
port = 3002

Step 4: Now let’s create the API first in the backend folder and then we will move to the front-end side. so first let’s connect the MongoDB, so before connecting the database we must install the mongoose package with the npm install mongoose command.

and then create a new file config/db.js and make the database connection

const mongoose = require("mongoose");
require('dotenv').config();

// Replace this with your MONGOURI.
const MONGOURI = process.env.DATABSE_CONNECTION
const InitiateMongoServer = async () => {
  try {
    await mongoose.connect(MONGOURI, {
      useNewUrlParser: true
    });
    console.log("Connected to DB !!");
  } catch (e) {
    console.log(e);
    throw e;
  }
};
module.exports = InitiateMongoServer;

Step 5: Now move on to the app.js file and make the changes. and set the routes.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
const bodyParser = require("body-parser");
var logger = require('morgan');
const cors = require("cors"); // Enable All CORS Requests
require('dotenv').config();
const InitiateMongoServer = require("./config/db");
// Initiate Mongo Server
InitiateMongoServer();

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

app.use(express.static(path.join(__dirname, 'public')));

// Enable All CORS Requests
app.use(cors());
//All routes
app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};
  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Step 6: so now let’s create a model folder and create User.js file /model/User.js so this can connect with MongoDB for crud operation.

const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
  // username: {type: String, required: true},
  firstname : { type: String, required: true },
  lastname : { type: String, required: true },
  email: { type: String, required: true },
  // password: { type: String, required: true },
  role: { type: String, require: true, 
          enum: ['Artist','Designer','Art Manager'] 
        },
  image: { type: String, default: null },
  status: { type: Number, default: 1, enum: [1, 0] },
  is_deleted: { type: Number, default: 0, enum: [1, 0] },
  createdAt: { type: Date, default: Date.now },
  modifiedAt: { type: Date, default: Date.now },
});

// export model user with UserSchema
module.exports = mongoose.model("user", UserSchema);

So on the above code, we can see that first import the mongoose command that we have installed in the beginning. and others code that the Schema where mention fields and validation so it will create the table with fields in the database.

Step 7: Now let’s create the routes folder and users.js file /routes/users.js so in this folder we will create all the routes for users listing, create user, edit and delete the user.

For Listing Users

before implementing the listing user code we just need to import the model created on step:6 and install the express-validator package for validation

var express = require('express');
var router = express.Router();
const { check, validationResult} = require("express-validator");
const User = require("../model/User");

/* GET users listing. */
router.get('/', function(req, res) {
  try {
    User.find().then(users => {
      res.status(200).send({ 
        users : users,
        message : "User has been fetch successfully!"
      });
    }).catch(err => {
      res.status(500).send({
         message: err.message || "Some error occurred while retrieving notes."
      });
    });
  } catch (err) {
    res.status(500).send({message : "Error in Saving"});
  }
});

For Create User

/**
 * @method - POST
 * @param - /crete
 * @description - User Create
 */

router.post("/create",[
      check("firstname", "Please Enter a Valid Firstname").not().isEmpty(),
      check("lastname", "Please Enter a Valid Lastname").not().isEmpty(),
      check("role", "Please Enter a Valid role").not().isEmpty(),
      check("email", "Please enter a valid email").isEmail(),
    ],

    async (req, res) => {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({
          errors: errors.array(),
        });
      }
      
        const {  firstname, lastname, role, email } = req.body;
        try {
            let user = await User.findOne({ email});
            if (user) {
                return res.status(400).json({
                    message: "User Already Exists"
                });
            }
            if(role == 'Art Manager')
            {
              let result = await User.findOne({ role : 'Art Manager'});
              if (result) {
                  return res.status(400).json({
                      message: "Art Manager Already Exists"
                  });
              }
            }
            user = new User({firstname , lastname, role, email});
            await user.save();            

            const payload = { user: { id: user.id} };
            return res.status(200).json({
              user : user,
              message : 'User has been created successfully!'
            });
        } catch (err) {
          console.log('err',err);
          res.status(500).send({message : "Error in Saving"});
        }
    });

For Edit  User

/**
 * @method - POST
 * @param - /update
 * @description - User Update
 */

router.post("/update/:Id",[
      check("firstname", "Please Enter a Valid Firstname").not().isEmpty(),
      check("lastname", "Please Enter a Valid Lastname").not().isEmpty(),
      check("role", "Please Enter a Valid role").not().isEmpty(),
      check("email", "Please enter a valid email").isEmail(),
    ],

    async (req, res) => {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({
          errors: errors.array()
        });
      }
     
      let user = await User.findById(req.params.Id);
      if (!user && user.role != 'Art Manager') {
        let userdata = await User.findOne({ email});
        if (userdata) {
            return res.status(400).json({
                message: "User Already Exists"
            });
        }
        if(role == 'Art Manager'){
          let result = await User.findOne({ role : 'Art Manager'});
          if (result) {
              return res.status(400).json({
                  message: "Art Manager Already Exists"
              });
          }
        }
      }

      const { firstname, lastname, role, email } = req.body;
      try {
        // Find user and update it with the request body
        User.findByIdAndUpdate(req.params.Id, {
          firstname :  firstname,
          lastname : lastname,
          role : role,
          email : email
          }, {new: true}).then(user => {
            if(!user) {
                return res.status(404).send({
                    message: "User not found with id " + req.params.Id
                });
            }
            res.status(200).send({
              user : user,
              message : 'User has been updated successfuly!'
            });
          }).catch(err => {
            if(err.kind === 'ObjectId') {
                return res.status(404).send({
                    message: "User not found with id " + req.params.Id
                });               
            }
            return res.status(500).send({
                message: "Error updating user with id " + req.params.Id
            });
          });
      } catch (err) {
          res.status(500).send({message:"Error in updating"});
      }
    }
);

Delete User

/**
 * @method -  GET
 * @description - DELETE User
 * @param - /user/delete
 */

router.get("/delete/:Id", async (req, res) => {
  try {
    User.findByIdAndRemove(req.params.Id)
    .then(user => {
        if(!user) {
            return res.status(404).send({
                message: "User not found with id " + req.params.Id
            });
        }
        res.send({message: "User deleted successfully!"});
    }).catch(err => {
        if(err.kind === 'ObjectId' || err.name === 'NotFound') {
            return res.status(404).send({
                message: "User not found with id " + req.params.Id
            });                
        }
        return res.status(500).send({
            message: "Could not delete user with id " + req.params.Id
        });
    });
  } catch (e) {
    res.send({ message: "Error in Fetching user" });
  }
});

now lets’s add the last line

module.exports = router;

Step 8: so now let’s come to the src folder of Angular where already app.module.ts  & one component was created, so we are going to create a new folder for users and list components to display the user listing.

Step 9: So now lets we set the path in the environment file for the API.

export const environment = {
  production: false,
   API_URL : 'http://localhost:3002'
};

Step 10: So now let’s create the services folder with user services where API is called.

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http";
import {environment} from '../environments/environment';
import {  throwError, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { User } from '../app/models/users';

@Injectable({
  providedIn: 'root'
})

export class UserService {
  private apiServer = environment.API_URL ;
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  }

  constructor(private httpClient: HttpClient) { }
  create(User): Observable<User> {
    return this.httpClient.post<User>(this.apiServer + '/users/create', 
    JSON.stringify(User), this.httpOptions)
    .pipe(
      catchError(this.errorHandler)
    )
  }

  getById(id): Observable<User> {
    return this.httpClient.get<User>(`${this.apiServer}/users/get/${id}`)
    .pipe(
      catchError(this.errorHandler)
    )
  }

  getAll(): Observable<User[]> {
    return this.httpClient.get<User[]>(this.apiServer + '/users').pipe(
      catchError(this.errorHandler)
    )
  }

  update(id, User): Observable<User> {
    return this.httpClient.post<User>(`${this.apiServer}/users/update/${id}`, JSON.stringify(User), this.httpOptions)
    .pipe(
      catchError(this.errorHandler)
    )
  }

  delete(id){
    return this.httpClient.get<User>(`${this.apiServer}/users/delete/${id}`, this.httpOptions)
    .pipe(
      catchError(this.errorHandler)
    )
  }

  errorHandler(error) {
     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}`;
     }

     return throwError(errorMessage);
  }
}

Step 11: Now go to the user’s listing component.
User Listing & Searching & Delete

import { Component, OnInit } from '@angular/core';
import { UserService } from '../../../services/users.services';
import { User } from '../../models/users';
import Swal from 'sweetalert2'
import { ToastrService } from 'ngx-toastr';

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

export class ListComponent implements OnInit {
  users: User[] = [];
  allusers: User[] = [];
  role = 'All';
  searchText;
  constructor(private userServices : UserService, private toastr: ToastrService) { }

  ngOnInit() {
    this.getAll();
  }

  //get all users
  getAll(){
    this.userServices.getAll().subscribe((data: User[])=>{
      this.users = data['users'];
      this.allusers = data['users'];
    })
  }

  // Delete used based on Id
  onDelete(user){
    Swal.fire({
      title: 'Are you sure?',
      text: 'You will not be able to recover this user!',
      icon: 'warning',
      showCancelButton: true,
      confirmButtonText: 'Yes, delete it!',
      cancelButtonText: 'No, keep it'
    }).then((result) => {
      if (result.value) {
        this.userServices.delete(user._id).subscribe(response=>{
          this.toastr.success('User has been deleted successfully', 'Success!'); 
          this.getAll();
        });
      }
    }).catch(error =>{
      this.toastr.error('Please try again!', 'Error!');
    })
  }

  // Filter data based role from existing data
  onFilter(event){
    if(event.target.value == 'Artist'){
      this.users = this.allusers.filter(user => user.role == 'Artist');
    }else if(event.target.value == 'Designer'){
      this.users = this.allusers.filter(user => user.role == 'Designer');
    }else if(event.target.value == 'Art Manager'){
      this.users = this.allusers.filter(user => user.role == 'Art Manager');
    }else if(event.target.value == 'All'){
      this.users = this.allusers;
    }else{
      this.users = [];
    }
  }
}

 HTML file

<div class="container">
    <h1>Users</h1>
    <div class="row">
        <div class="col-sm">
            <div class="form-group">
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                      <span class="input-group-text" id="basic-addon1">Role</span>
                    </div>
                    <select [(ngModel)]="role" class="form-control" (change)="onFilter($event)">
                        <option value="All">All</option>
                        <option value="Artist">Artist</option>
                        <option value="Designer">Designer</option>
                        <option value="Art Manager">Art Manager</option
                    </select>
                  </div>
            </div>
        </div>

        <div class="col-sm">
            <input type="text" class="form-control" placeholder="Search.." [(ngModel)]="searchText" autocomplete="off"/>
        </div>
      </div>
    <table class="table">
        <thead>
            <tr>
                <th>#</th>
                <th>Firstname</th>
                <th>Lastname</th>
                <th>Email</th>
                <th>Role</th>                            
                <th>
                    <button type="button" class="btn btn-primary" routerLink="/create">Add New</button>
                </th>
            </tr>
        </thead>
        <tbody>
            <tr *ngFor="let user of users| filter : searchText:'email' ; let i = index ">
                <td [ngClass]="user.role">{{i+ 1}}</td>
                <td [ngClass]="user.role">{{user.firstname}}</td>
                <td [ngClass]="user.role">{{user.lastname}}</td>
                <td [ngClass]="user.role">{{user.email}}</td>
                <td [ngClass]="user.role">{{user.role}}</td>
                <td [ngClass]="user.role">
                    <button type="button" class="btn" title="Edit" [routerLink]="['/update/', user._id]"><i class="fas fa-edit"></i></button> &nbsp;
                    <button type="button" class="btn" title="Delete" (click)="onDelete(user)"><i class="fas fa-trash"></i></button>
                </td>
            </tr>
        </tbody>
    </table>
</div>

Step 12: Now let’s create the create and edit page where we use the same component for both.

Create  & Edit 

manage.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../../../services/users.services';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute} from '@angular/router';
import { ToastrService } from 'ngx-toastr';

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

export class ManageComponent implements OnInit {
  userForm: FormGroup;
  submitted = false;
  userId;
  addMode = true;
  user : any = [];

  constructor(public fb: FormBuilder, private route: ActivatedRoute, private toastr: ToastrService,
    private router: Router, public useService: UserService) {
      this.route.paramMap.subscribe(params => {
        this.userId = params.get('Id');
        if(this.userId){
          this.addMode = false;
          this.getUserById(this.userId);
        }
     });
  }

  ngOnInit() {
    this.userForm = this.fb.group({
      firstname: ['', Validators.required],
      lastname: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      role: ['', Validators.required],   
    })
  }

  // convenience getter for easy access to form fields
  get f() { return this.userForm.controls; }

  //on registration form submit
  async onSubmit() {
    this.submitted = true;
    // stop here if form is invalid
    if (this.userForm.invalid) {
      return;
    }

    if(this.addMode){
      this.useService.create(this.userForm.value).subscribe(response =>{
        this.toastr.success('User has been created successfully', 'Success!'); 
        this.router.navigateByUrl('/');
      },(error) =>{
        this.toastr.error('Please try again!', 'Error!');
      });
    }else{
      this.useService.update(this.userId, this.userForm.value).subscribe(response =>{
        this.toastr.success('User has been updated successfully', 'Success!');
        this.router.navigateByUrl('/');
      },(error) =>{
        this.toastr.error('Please try again!', 'Error!');
      });
    }
  }

  getUserById(Id){
    this.useService.getById(Id).subscribe(response => {
      this.user = response['user'];
    })
  }
}

manage.component.html

<div class="container">
    <h1 class="Registration_title" style="text-align: center;" *ngIf="addMode">Create New</h1>
    <h1 class="Registration_title" style="text-align: center;" *ngIf="!addMode">Edit</h1>
    <form  [formGroup]="userForm" (ngSubmit)="onSubmit()">
        <!-- firstname -->
        <div class="form-group">
            <label for="ffirstname">Firstname </label>
            <input type="text" id="firstname" formControlName="firstname" [(ngModel)]="user.firstname"
                class="form-control" [ngClass]="{ 'is-invalid': submitted && f.firstname.errors }">
            <div *ngIf="submitted && f.firstname.errors" class="invalid-feedback">
                <div *ngIf="f.firstname.errors.required">firstname is required</div>
            </div>
        </div>

        <!-- lastname -->
        <div class="form-group">
            <label for="lastname">Lastname</label>
            <input type="text" ng-model="input" id="lastname" [(ngModel)]="user.lastname" formControlName="lastname"
                class="form-control" [ngClass]="{ 'is-invalid': submitted && f.lastname.errors }">
            <div *ngIf="submitted && f.lastname.errors" class="invalid-feedback">
                <div *ngIf="f.lastname.errors.required">lastname is required</div>
            </div>
        </div>

        <!-- email -->
        <div class="form-group">
            <label for="email">Email</label>
            <input type="text" id="email" formControlName="email"  [(ngModel)]="user.email" class="form-control"
                [ngClass]="{ 'is-invalid': submitted && f.email.errors }" />
            <div *ngIf="submitted && f.email.errors" class="invalid-feedback">
                <div *ngIf="f.email.errors.required">Email is required</div>
            </div>
        </div>
        <div class="form-group">
            <label for="Email">Role</label>
            <select formControlName="role" [(ngModel)]="user.role" class="form-control">
                <option disabled>Select Role</option>
                <option value="Artist">Artist</option>
                <option value="Designer">Designer</option>
                <option value="Art Manager">Art Manager</option>
            </select>
            <div *ngIf="submitted && f.role.errors" class="invalid-feedback">
                <div *ngIf="f.role.errors.required">Role is required</div>
            </div>
        </div>

        <button type="submit" class="btn btn-primary registerbtn">Submit</button> &nbsp;
        <button type="submit" class="btn btn-secondary" routerLink="/">Cancel</button>
    </form>
</div>

 

Step 13: Now we need to use the filter on the listing page so for that need to create the filter pipe in pipe/filter.pipe.ts

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({
    name: 'filter'
})
export class FilterPipe implements PipeTransform {
    transform(items: any, term: any, prop: string): any {
        if (!term || !prop) return items;
        return items.filter((item) => 
            item[prop].toString().toLowerCase().includes(term.toLowerCase()));
      }
}

Step 14: Now let’s implement all components in routing so it will move on different pages like create, edit, etc..

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ListComponent } from './users/list/list.component';
import { ManageComponent } from './users/manage/manage.component';

const routes: Routes = [
  { path: '' ,component: ListComponent},
  { path: 'create', component: ManageComponent },
  { path: 'update/:Id', component: ManageComponent } 
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

export class AppRoutingModule { }

 

Step 15: Now let’s change the command and build the path of angular

Now open the terminal and press the npm start command so it will make a build file and run the backend as well. and open the local URL in the browser.

Leave a Comment

Your email address will not be published. Required fields are marked *