This page looks best with JavaScript enabled

Angular 7 CRUD With Node.JS API

 ·  ☕ 14 min read  ·  ✍️ Adesh

In the previous post, I created the Node.JS API endpoints, and described everything related to Node.JS and MongoDB database. Please go through my previous post before reading this blog for better understanding.

How to create Restful CRUD API with Node.js MongoDB and Express.js

Angular 7 Prerequisites

Before start writing the Angular 7 app, please make sure that, you have latest version of:

  • Node.JS and NPM installed on your system

  • Angular 7 CLI installed on your system

You can go through below links for more details about updating Angular 6 to Angular 7, and creating your first Angular 7 app.

Update Angular 6 to Angular 7 version

Creating your first app in Angular 7

MongoDB Backend Overview

Let me introduce you about the MongoDB Document structure. We have Products collection in our database, and this Products collection have multiple documents related to various products.

This is the screenshot of our MongoDB Products collection.

MongoDB Products Collection

Products Collection

And this is the screenshot of each Product document.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "_id": {
    "$oid": "5c3162ed7829481868c3afe9"
  },
  "title": "HP",
  "description": "15 inch HP Laptop",
  "price": 1500,
  "company": "HP",
  "createdAt": {
    "$date": "2019-01-06T02:07:41.477Z"
  },
  "updatedAt": {
    "$date": "2019-01-06T02:07:41.477Z"
  },
  "__v": 0
}

Node.JS Rest API Endpoints

You can get all details of Node.JS Rest API in the above link, which I shared on the top of this post. Let me share again, which endpoints we have for our application. These are the list of our API endpoints.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Create a new Product
app.post("/products", products.create);

// Retrieve all Products
app.get("/products", products.findAll);

// Retrieve a single Product with productId
app.get("/products/:productId", products.findOne);

// Update a Note with productId
app.put("/products/:productId", products.update);

// Delete a Note with productId
app.delete("/products/:productId", products.delete);

Creating Angular 7 Project

Let’s start with our Angular 7 app. Using above Node.JS API endpoints, we are going to perform CRUD operation. This will be the screen of our Angular app.

Angular 7 App with CRUD

Create your angular app using the ng new <app-name> command in your terminal.

1. Creating Model Class In Angular

After creating our app, let’s first create our Product Model class as per our database Product Document properties.

ProductModel.ts

1
2
3
4
5
6
7
export class ProductModel {
  _id: string;
  title: String;
  description: String;
  price: Number;
  company: String;
}

2. Creating Service In Angular

Let’s create our Product Service class, which will interact with our Node.JS endpoints for all product related operations.

product.service.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { ProductModel } from "./ProductModel";

@Injectable({
  providedIn: "root",
})
export class ProductService {
  constructor(private http: HttpClient) {}

  baseurl: string = "http://localhost:3000/";

  getAllProducts() {
    return this.http.get<ProductModel[]>(this.baseurl + "Products");
  }

  getProductById(id: string) {
    return this.http.get<ProductModel>(this.baseurl + "Products" + "/" + id);
  }

  addProduct(product: ProductModel) {
    return this.http.post(this.baseurl + "Products", product);
  }

  deleteProduct(id: string) {
    return this.http.delete(this.baseurl + "Products" + "/" + id);
  }

  updateProduct(product: ProductModel) {
    return this.http.put(
      this.baseurl + "Products" + "/" + product._id,
      product
    );
  }
}

We have imported HttpClient and ProductModel in our service class.

This is our service url endpoint.

baseurl: string = “http://localhost:3000/";

In this class, we have different methods. These are:

  • getAllProducts(): This method is used to get all products. We are using HTTP GET method of HttpClient object with service endpoint.

  • getProductById(id: string): This method is used to get a product by product id. We are using same HTTP GET method of HTTPClient object with service endpoint. Product ID is passed in service url.

  • addProduct(product: ProductModel): This method is used to add product. We are using HTTP POST method of HTTPClient object with service endpoint. Product object will be send in request body.

  • deleteProduct(id: string): This method is used to delete the product. We are using HTTP DELETE method of HTTPClient object with the service endpoint. We are just passing the Product ID in the service url.

  • updateProduct(product: ProductModel): This method is used to update the product. We are using HTTP PUT method of HTTPClient object with the service endpoint. We are passing ProductID in service endpoint url, and new product in the request body.

Import Product Service in app.module

Now, after creating the product service, we have to import this in our app.module.ts file. Let’s do it now.

app.module.ts

Add this line on the top of app.module.ts file.

And this ProductService in Providers[] array.

3. Creating Component For Listing All Products

First of all, we are going to fetch all the products in our database. Create list-products component, and add this to our routing module. This component will fetch all the products from the database and display it in tabular form. Add routing details of this component in our route file.

app-routing.module.ts

Import ListProductsComponent in our routing file.

And, add this in routes constant.

This is our list-products component html file.

list-products.component.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<div class="container col-md-12">
  <div>
    <button (click)="addProduct()" class="btn btn-info">Add Product</button>
  </div>
  <h2>All Products</h2>
  <div class="table-responsive table-container">
    <table class="table table-dark table-hover table-striped">
      <thead class="thead-light">
        <tr>
          <th>Title</th>
          <th>Description</th>
          <th>Price($)</th>
          <th>Company</th>
          <th>Action</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let product of products">
          <td>{{product.title}}</td>
          <td>{{product.description}}</td>
          <td>{{product.price | currency : "USD" : true}}</td>
          <td>{{product.company}}</td>
          <td>
            <button (click)="deleteProduct(product)" class="btn btn-info">
              Delete
            </button>
            <button
              (click)="updateProduct(product)"
              style="margin-left: 20px;"
              class="btn btn-info"
            >
              Edit
            </button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

list-products.component.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { ProductModel } from "../ProductModel";
import { ProductService } from "../product.service";

@Component({
  selector: "app-list-products",
  templateUrl: "./list-products.component.html",
  styleUrls: ["./list-products.component.css"],
})
export class ListProductsComponent implements OnInit {
  products: ProductModel[];

  constructor(private productService: ProductService, private router: Router) {}

  ngOnInit() {
    this.getAllProducts();
  }

  getAllProducts(): void {
    this.productService.getAllProducts().subscribe((data) => {
      this.products = data;
    });
  }

  addProduct(): void {
    this.router.navigate(["add-product"]);
  }

  deleteProduct(product: ProductModel) {
    this.productService.deleteProduct(product._id).subscribe((data) => {
      console.log(data);
      this.getAllProducts();
    });
  }

  updateProduct(product: ProductModel) {
    localStorage.removeItem("productId");
    localStorage.setItem("productId", product._id);
    this.router.navigate(["edit-product"]);
  }
}

To get the list of all products, we have created getAllProducts() function, and call it in ngOnInit().

4. Creating Component For Adding Product

This component will be used to add a product in our database. Add routing details of this component in our route file.

app-routing.module.ts

Add this line on the top of app-routing.module.ts file.

And, add this in routes constant.

This is our add-product component file.

add-product.component.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<div class="col-md-3">
  <h2 class="text-center">Add Product</h2>
  <form [formGroup]="addForm" (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label>Title:</label>
      <input
        type="text"
        formControlName="title"
        placeholder="Title"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.title.errors }"
      />
      <div *ngIf="submitted && f.title.errors">
        <div *ngIf="f.title.errors.required">Title is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Description:</label>
      <input
        type="text"
        formControlName="description"
        placeholder="Description"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.description.errors }"
      />
      <div *ngIf="submitted && f.description.errors">
        <div *ngIf="f.description.errors.required">Description is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Price ($):</label>
      <input
        type="text"
        formControlName="price"
        placeholder="Price"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.price.errors }"
      />
      <div *ngIf="submitted && f.price.errors">
        <div *ngIf="f.price.errors.required">Price is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Company:</label>
      <input
        type="text"
        formControlName="company"
        placeholder="Company"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.company.errors }"
      />
      <div *ngIf="submitted && f.company.errors">
        <div *ngIf="f.company.errors.required">Company is required</div>
      </div>
    </div>

    <button type="submit" class="btn btn-info">Save</button>
  </form>
</div>

add-product.component.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { Component, OnInit } from "@angular/core";
import {
  FormBuilder,
  FormGroup,
  FormControl,
  Validators,
} from "@angular/forms";
import { ProductService } from "../product.service";
import { first } from "rxjs/operators";
import { Router } from "@angular/router";

@Component({
  selector: "app-add-product",
  templateUrl: "./add-product.component.html",
  styleUrls: ["./add-product.component.css"],
})
export class AddProductComponent implements OnInit {
  constructor(
    private formBuilder: FormBuilder,
    private router: Router,
    private productService: ProductService
  ) {}

  addForm: FormGroup;
  submitted = false;

  ngOnInit() {
    this.addForm = this.formBuilder.group({
      _id: [],
      title: ["", Validators.required],
      description: ["", Validators.required],
      price: ["", Validators.required],
      company: ["", Validators.required],
    });
  }

  onSubmit() {
    this.submitted = true;
    if (this.addForm.valid) {
      this.productService.addProduct(this.addForm.value).subscribe((data) => {
        console.log(data);
        this.router.navigate([""]);
      });
    }
  }

  // get the form short name to access the form fields
  get f() {
    return this.addForm.controls;
  }
}

In this component class, we have used Validators to check whether all required fields are filled or not. When user click on Add Product button on Product List page, user will be navigating to the Add Product page.

Angular 7 CRUD App with Node.Js API

Add Product Page

In the above pic, if you try to save without filing all the fields, it will red highlight the textbook and let you not to save the product. So, fill all the fields and save the new product. As soon as your product is saved, you will be redirecting again to the all products page with newly product added.

Also, notice the following code in list-products.component.html file. We have (click) event on the Add Product button, and calling the addProduct() method of list-products.component.ts class. In this method, we are simply navigating to our route add-product, and opening the add-product component page.

1
2
3
addProduct(): void {
    this.router.navigate(['add-product']);
  }

5. Delete Product

In order to delete any product, we don’t need to create any component. For each product in grid on list-products.component.html component, we have Delete button attached to it. There is an deleteProduct(product) method on (click) event of this Delete button. deleteProduct(product) method is defined in list-products.component.ts file.

1
2
3
4
5
6
deleteProduct(product: ProductModel){
    this.productService.deleteProduct(product._id).subscribe(data=>{
      console.log(data);
      this.getAllProducts();
    });
  }

After deleting the product, we are fetching all the products again by calling this.getAllProducts() method.

6. Creating Component For Updating Product

In order to update any product, we are going to create another component. Create component edit-product in your app. Add creating your component, we have to define the route as well in your app. Open the app-routing.module.ts file and import this component there.

app-routing.module.ts

Add the route in routes constant.

edit-product.component.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<div class="col-md-3">
  <h2 class="text-center">Edit Product</h2>
  <form [formGroup]="editForm" (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label>Title:</label>
      <input
        type="text"
        formControlName="title"
        placeholder="Title"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.title.errors }"
      />
      <div *ngIf="submitted && f.title.errors">
        <div *ngIf="f.title.errors.required">Title is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Description:</label>
      <input
        type="text"
        formControlName="description"
        placeholder="Description"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.description.errors }"
      />
      <div *ngIf="submitted && f.description.errors">
        <div *ngIf="f.description.errors.required">Description is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Price ($):</label>
      <input
        type="text"
        formControlName="price"
        placeholder="Price"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.price.errors }"
      />
      <div *ngIf="submitted && f.price.errors">
        <div *ngIf="f.price.errors.required">Price is required</div>
      </div>
    </div>

    <div class="form-group">
      <label>Company:</label>
      <input
        type="text"
        formControlName="company"
        placeholder="Company"
        class="form-control"
        [ngClass]="{ 'is-invalid': submitted && f.company.errors }"
      />
      <div *ngIf="submitted && f.company.errors">
        <div *ngIf="f.company.errors.required">Company is required</div>
      </div>
    </div>

    <button type="submit" class="btn btn-info">Update</button>
  </form>
</div>

Our edit-product component html file is similar to add-product component html file. So, you can simply copy paste the same html here.

edit-product.component.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { Component, OnInit } from "@angular/core";
import {
  FormBuilder,
  FormGroup,
  FormControl,
  Validators,
} from "@angular/forms";
import { ProductService } from "../product.service";
import { first } from "rxjs/operators";
import { Router } from "@angular/router";
import { ProductModel } from "../ProductModel";

@Component({
  selector: "app-edit-product",
  templateUrl: "./edit-product.component.html",
  styleUrls: ["./edit-product.component.css"],
})
export class EditProductComponent implements OnInit {
  product: ProductModel;
  editForm: FormGroup;
  submitted = false;

  constructor(
    private formBuilder: FormBuilder,
    private router: Router,
    private productService: ProductService
  ) {}

  ngOnInit() {
    let productId = localStorage.getItem("productId");
    if (!productId) {
      alert("Something wrong!");
      this.router.navigate([""]);
      return;
    }

    this.editForm = this.formBuilder.group({
      _id: [],
      title: ["", Validators.required],
      description: ["", Validators.required],
      price: ["", Validators.required],
      company: ["", Validators.required],
    });

    this.productService.getProductById(productId).subscribe((data) => {
      console.log(data);
      this.editForm.patchValue(data); //Don't use editForm.setValue() as it will throw console error
    });
  }

  // get the form short name to access the form fields
  get f() {
    return this.editForm.controls;
  }

  onSubmit() {
    this.submitted = true;
    if (this.editForm.valid) {
      this.productService
        .updateProduct(this.editForm.value)
        .subscribe((data) => {
          console.log(data);
          this.router.navigate([""]);
        });
    }
  }
}

Here, we are using localStorage.getItem() to get the productId, which was stored in our list-product.component.ts file using localStorage.setItem(). So, let’s first get the productId first from localStorage() in ngOnInit() method. See below code, how we are setting productId in localStorage(). After that, we are navigating to our edit component page.

list-product.component.ts

1
2
3
4
5
updateProduct(product: ProductModel){
    localStorage.removeItem("productId");
    localStorage.setItem("productId", product._id);
    this.router.navigate(['edit-product']);
  }

After retrieving the productId from localStorage, we are going to get the product using this productId in ngOnInit() function.

edit-product.component.ts

1
2
3
4
this.productService.getProductById(productId).subscribe((data) => {
  console.log(data);
  this.editForm.patchValue(data); //Don't use editForm.setValue() as it will throw console error
});

On successful, we are setting the form values with the response data using patchValue() method.

Angular 7 CRUD App with Node.js

Edit Product

Update the values in the form and click on Update button. It will call the onSubmit() function of edit-product.component.ts class file. After updating the values in database, it will be redirected to product listing component page.

So, we have done with our CRUD operation on our product model. Let me show you complete app-routing and app.module files.

Angular 7 Routing: app-routing.module.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { CommonModule } from "@angular/common";
import { ListProductsComponent } from "./list-products/list-products.component";
import { AddProductComponent } from "./add-product/add-product.component";
import { EditProductComponent } from "./edit-product/edit-product.component";

const routes: Routes = [
  { path: "add-product", component: AddProductComponent },
  { path: "edit-product", component: EditProductComponent },
  { path: "", component: ListProductsComponent, pathMatch: "full" },
];

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

Angular 7 Module: app.module.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";
import { ReactiveFormsModule } from "@angular/forms";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { ProductService } from "./product.service";
import { ListProductsComponent } from "./list-products/list-products.component";
import { AddProductComponent } from "./add-product/add-product.component";
import { EditProductComponent } from "./edit-product/edit-product.component";

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

Install Bootstrap and FontAwesome in Angular 7

Use this npm command to install the Bootstrap and FontAwesome in your project.

1
npm install bootstrap font-awesome

Importing the Bootstrap and FontAwesome

After installation, you can add these css files in your styles.css file.

1
2
3
/* You can add global styles to this file, and also import other style files */
@import "~bootstrap/dist/css/bootstrap.css";
@import "~font-awesome/css/font-awesome.css";

Angular 7 Project Structure

This will be our app structure.

Angular App Project Structure

Angular App Project Structure

Download

You can download all the project files from GitHub.

Download Project Files

Summary

In this blog, we learned about how to perform CRUD operation on our MongoDB database using Node.Js Api. I also shared the project files on Github, from where you can view and download it.

Further Reading

How to create Restful CRUD API with Node.js MongoDB and Express.js

Eleven Ways To Learn Javascript Array Iteration Effectively

JavaScript Fundamentals Every Beginner Should Know

Share on

Adesh
WRITTEN BY
Adesh
Technical Architect