Angular 8 CRUD: Angular 8 Tutorial For Beginners

Angular 8 CRUD is a basic operation to learn Angular from scratch. We will learn how to build a small web application that inserts, read data, update and delete data from the database. You will learn how to create a MEAN Stack web application. In this Angular 8 Tutorial Example,  you will learn a new framework by building a crud application.

New features of Angular 8

You check out the new features in brief on my Angular 8 New Features post.

I have designed this Angular 8 CRUD Tutorial, especially for newcomers, and it will help you up and running with the latest version of Angular, which is right now 8.

The workflow of Angular crud example

We will create two separate projects. One is for Angular Frontend, and one is for Node.js and Express. That means one for the frontend and one for the back end.

We will create the backend API, which deals with storing, modifying, and deleting the form values, and the frontend will consume that API; for example, it shows the data from the backend.

For this example, I am using the following tech stacks with their versions.

  1. Node v11.3.0
  2. NPM v6.9.0
  3. AngularCLI v8.0.1
  4. MongoDB shell version v3.6.3
  5. MongoDB version v3.6.3
  6. Mac OS Mojave

In this blog, I have previously written Angular 7 CRUD and Angular 6 CRUD tutorials in deep. Now, the Angular community has released the next version, which is Angular 8.

If you do not know how to upgrade Angular CLI to version 8, check out my Angular CLI 8 upgrade tutorial. In this MEAN Stack tutorial, we will see Angular Routing, Angular Forms, and on the back end side, we use Node.js and Express.js to handle the data, and to store the data, we will use MongoDB.

Angular 8 CRUD Example

If you are looking to find a tutorial on building a crud application in Angular 8 and mean stack app, then continue this post. This post is a comprehensive guide to take you on a tour where you will learn how to send network requests to node servers, how to handle the response, how to display data in the front end, and so on. The post is lengthy but stays with me. It is gonna be a hell of a lot of fun.

We will use Node.js as a platform and Express.js, a web framework built on top of Node.js, and MongoDB as a NoSQL Database.

But, you will learn more concepts like project structure, modules, dependency injection, service, components, routing, http requests, and much more. It is like you will learn almost half the framework by doing this project.

In this tutorial, we will use Angular 8. So if you are not so sure what Angular CLI version you are using, then type the following command, which will tell us the Angular CLI version.

ng --version

Angular 8 Tutorial With Example

Angular 8 sample project description

We will create a demo project where users can save their ProductName, Product Description, and ProductPrice from the Angular form and submit the form to the node.js server.

If the values are incorrect, they will validate at the frontend, and the form will not submit.

We will not validate the field values at the backend because it will make this tutorial more lengthy. If you want to create a validation at the backend, check out my express form validation tutorial.

If all the values are perfect, it will send the form values to the Node.js backend API, and it will store the values inside the MongoDB database.

Our Angular 8 CRUD Tutorial will be very long, so please bear with me for this long journey.

Step 1: Create Angular 8 project

Let’s create an Angular 8 project using the following command.

Create Angular 8 Project

When you install a brand new Angular project, see the options they are providing while creating a new project. In this project, we need the routing module, so we will allow the Angular CLI to create a routing module for us.

I am using Visual Studio Code as a programming editor to develop Angular applications.

Now, go inside the folder and open the project inside the VSCode.

cd angular8tutorial
code .

At the time of installation, we have enabled routing for our angular app. You can check the file called app-routing.module.ts file inside the src >> app directory.

Next, install the Bootstrap 4 CSS Framework using the following command. It is not a necessary step, and you can choose your CSS Framework as well.

npm install bootstrap --save

Now, add it inside an angular.json file.

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

It will include a bootstrap CSS file, and we can use the bootstrap classes inside any file.

Start an Angular development server using the following command.

ng serve -o

The server starts at http://localhost:4200/. You can see the output in the browser. It is an initial Angular home screen.

Step 2: Create Angular components

Hit the following command to generate the Angular Components. We will perform to create, read, update operations. So we will create three components.

ng g c product-add --skipTests=true
ng g c product-get --skipTests=true
ng g c product-edit --skipTests=true
Option “spec” is deprecated in Angular 8: Use “skipTests” instead.
For this demo, we do not need the tests file. So, we will not create it.
All three components are automatically registered inside an app.module.ts file. Now, we need to configure the routing of angular components inside an app-routing.module.ts file.

Now, you can see an app-routing.module.ts file inside the src >> app folder file. It is created because while we were installing the angular app, we permitted angular cli to generate the routing file.

All three components are automatically registered inside an app.module.ts file. Now, we need to import and configure the routing of angular components inside the app-routing.module.ts file.

Write the following code inside an app-routing.module.ts file.

// app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ProductAddComponent } from './product-add/product-add.component';
import { ProductEditComponent } from './product-edit/product-edit.component';
import { ProductGetComponent } from './product-get/product-get.component';

const routes: Routes = [
  {
    path: 'product/create',
    component: ProductAddComponent
  },
  {
    path: 'edit/:id',
    component: ProductEditComponent
  },
  {
    path: 'products',
    component: ProductGetComponent
  }
];

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

Now, you can see inside the app.component.html file that the <router-outlet> directive is there. This directive helps us to render the different components based on route URI.

Step 3: Create Angular 8 Routing

Write the following code inside the app.component.html file.

<nav class="navbar navbar-expand-sm bg-light">
  <div class="container-fluid">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a routerLink="product/create" class="nav-link" routerLinkActive="active">
          Create Product
        </a>
      </li>
      <li class="nav-item">
        <a routerLink="products" class="nav-link" routerLinkActive="active">
          Products
        </a>
      </li> 
    </ul>
  </div>
</nav>

<div class="container">
  <router-outlet></router-outlet>
</div>

Save the file and go to the browser and click on two links. You can see that we can see the different components based on navigation.

Create an Angular Navigation

Step 4: Configure Angular routing progress indicator.

Type the following command to install the ng2-slim-loading-bar library.

npm install ng2-slim-loading-bar --save

So, if you install the third-party packages right now, it is not compatible with an Angular 8. To bridge the gap between Angular 8 and the third-party packages, we need to install the following library. That is it.

npm install rxjs-compat --save

Now, import the SlimLoadingBarModule inside an app.module.ts file.

// app.module.ts

import { SlimLoadingBarModule } from 'ng2-slim-loading-bar';

imports: [
    ...
    SlimLoadingBarModule
],

The next step is, include the styling that comes with the library inside src  >>  styles.css file.

@import "../node_modules/ng2-slim-loading-bar/style.css";

Step 5: Add Angular router events.

Angular RouterModule gives us the following event modules.

  1. NavigationStart
  2. NavigationEnd
  3. NavigationError
  4. NavigationCancel
  5. Router
  6. Event

Now, write the following code inside the  app.component.ts file.

// app.component.ts

import { Component } from '@angular/core';
import {SlimLoadingBarService} from 'ng2-slim-loading-bar';
import { NavigationCancel,
        Event,
        NavigationEnd,
        NavigationError,
        NavigationStart,
        Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular8tutorial';
  constructor(private loadingBar: SlimLoadingBarService, private router: Router) {
    this.router.events.subscribe((event: Event) => {
      this.navigationInterceptor(event);
    });
  }
  private navigationInterceptor(event: Event): void {
    if (event instanceof NavigationStart) {
      this.loadingBar.start();
    }
    if (event instanceof NavigationEnd) {
      this.loadingBar.complete();
    }
    if (event instanceof NavigationCancel) {
      this.loadingBar.stop();
    }
    if (event instanceof NavigationError) {
      this.loadingBar.stop();
    }
  }
}

In the above code, we have told the Angular 8 application that while we navigate from one component to another component, while we want to display the router progress indicator. 

When a user clicks the other route, the angular progress indicator start showing, and when the navigation is complete, it will stop displaying. So, it is the kind of best User experience for the user.

What it is doing in the code is that it intercepts the routing event and adds the loading bar component to the loading route so that we can see the routing indication every time we change the routes.

The final change is to display the routing indicator is that we need to insert an ng2-slim-loading-bar directive inside the app.component.html file at the top of the page.

<!-- app.component.html -->

<ng2-slim-loading-bar color="blue"></ng2-slim-loading-bar>
<nav class="navbar navbar-expand-sm bg-light">
  <div class="container-fluid">
    <ul class="navbar-nav">
      <li class="nav-item">
        <a routerLink="product/create" class="nav-link" routerLinkActive="active">
          Create Product
        </a>
      </li>
      <li class="nav-item">
        <a routerLink="products" class="nav-link" routerLinkActive="active">
          Products
        </a>
      </li> 
    </ul>
  </div>
</nav>

<div class="container">
  <router-outlet></router-outlet>
</div>

Save the file and go to the terminal to see if there is any error, and if there is no error, then go to the browser and change the routes, and you can see that now we can see the routing indicator while changing the different angular routes.

Step 6: Add bootstrap 4 to HTML form

Inside the product-add.component.html file, add the following bootstrap 4 form. We have three HTML fields for this demo.

<!-- product-add.component.html -->

<div class="card">
  <div class="card-body">
    <form>
      <div class="form-group">
        <label class="col-md-4">Product Name</label>
        <input type="text" class="form-control" />
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Description </label>
        <textarea class="form-control" rows = 7 cols = "5"></textarea>
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Price</label>
        <input type="text" class="form-control" />
      </div>
      <div class="form-group">
        <button type="submit" class="btn btn-primary">Create Product</button>
      </div>
    </form>
  </div>
</div>

Add Bootstrap Form

Step 7: Create Angular 8 form validation

For form validation, we have two options. Template-based and Reactive Forms Module. We will use the ReactiveFormsModule approach. Now, if you are new to Angular Form Validation, then please check out my this article Angular Form Validation Example Tutorial on this blog.

Now, import the ReactiveFormsModule inside the app.module.ts file. It comes with an Angular project by default.

// app.module.ts

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

imports: [
    ...
    ReactiveFormsModule
],

Now, we need to write a code for the app.component.ts file. Remember, this is not the template-driven form. So we need to add the code inside the app.component.ts file.

First, we import the FormGroup, FormBuilder, Validators modules from @angular/forms.

Also, create a constructor and instantiate the FormBuilder.

So write the following code inside the product-add.component.ts file.

// product-add.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup,  FormBuilder,  Validators } from '@angular/forms';

@Component({
  selector: 'app-product-add',
  templateUrl: './product-add.component.html',
  styleUrls: ['./product-add.component.css']
})
export class ProductAddComponent implements OnInit {

  angForm: FormGroup;
  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.angForm = this.fb.group({
      ProductName: ['', Validators.required ],
      ProductDescription: ['', Validators.required ],
      ProductPrice: ['', Validators.required ]
    });
  }

  ngOnInit() {
  }

}

We have used a form builder to handle all the validation. So in that constructor, we are creating the form with all the validation rules for particular fields. In our example, there are three fields.

If the input text is empty, it will give an error, and we need to display that error.

Now, write the following code inside the product-add.component.html file.

<!-- product-add.component.html -->

<div class="card">
  <div class="card-body">
    <form [formGroup]="angForm" novalidate>
      <div class="form-group">
        <label class="col-md-4">Product Name</label>
        <input type="text" class="form-control" 
          formControlName="ProductName" 
          #ProductName />
      </div>
      <div *ngIf="angForm.controls['ProductName'].invalid && (angForm.controls['ProductName'].dirty || angForm.controls['ProductName'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductName'].errors.required">
          Product Name is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Description </label>
        <textarea class="form-control" rows = 7 cols = "5"
        formControlName="ProductDescription" 
        #ProductDescription></textarea>
      </div>
      <div *ngIf="angForm.controls['ProductDescription'].invalid && (angForm.controls['ProductDescription'].dirty || angForm.controls['ProductDescription'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductDescription'].errors.required">
          Product Description is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Price</label>
        <input type="text" class="form-control" 
          formControlName="ProductPrice" 
          #ProductPrice
        />
      </div>
      <div *ngIf="angForm.controls['ProductPrice'].invalid && (angForm.controls['ProductPrice'].dirty || angForm.controls['ProductPrice'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductPrice'].errors.required">
          Product Price is required.
        </div>
      </div>
      <div class="form-group">
        <button type="submit" class="btn btn-primary"
        [disabled]="angForm.pristine || angForm.invalid" >
          Create Product
        </button>
      </div>
    </form>
  </div>
</div>

Save the file and go to the browser, and you can see if you do not put any value inside the input box, you will see the errors.

Add Angular 8 Form Validation

Step 8: Add and configure the HttpClientModule

Most front-end applications communicate with the backend services over an HTTP protocol. Modern browsers support two different APIs for making HTTP requests: the XMLHttpRequest interface and the fetch API.

The HttpClient in @angular/common/http offers a simplified client HTTP API for Angular applications that rests on the XMLHttpRequest interface exposed by browsers.

Additional benefits of the HttpClient include the testability features, typed request and response objects, request and response interception, the Observables APIs, and smooth error handling.

Import the HttpClientModule inside an app.module.ts file.

// app.module.ts

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

imports: [
   ...
    HttpClientModule
 ],

Step 9: Create a typescript model file.

Inside the src >> app folder, create one file called Product.ts and add the following code.

// Product.ts

export default class Product {
  ProductName: string;
  ProductDescription: string;
  ProductPrice: number;
}

Step 10: Create an Angular Service file.

Type the following command to generate the service file.

The primary use of the service file is that we are adding all of our AJAX code inside that file. So, it sends an ajax request to the backend server and retrieves the data from the backend server.

ng g service products --skipTests=true

So, your necessary products.service.ts file looks like this.

// products.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ProductsService {

  constructor() { }
}

Now, import the products.service.ts file into the app.module.ts file.

// app.module.ts

import { ProductsService } from './products.service';

providers: [ ProductsService ],

Step 11: Submit form values to node server

We need to write the code that will send the HTTP POST request with the data to the Node.js server and store the data into the MongoDB database.

Write the following code inside the products.service.ts file.

// products.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ProductsService {

  uri = 'http://localhost:4000/products';

  constructor(private http: HttpClient) { }

  addProduct(ProductName, ProductDescription, ProductPrice) {
    const obj = {
      ProductName,
      ProductDescription,
      ProductPrice
    };
    console.log(obj);
    this.http.post(`${this.uri}/add`, obj)
        .subscribe(res => console.log('Done'));
  }
}

We have defined our backend API URL, but we have not created any backend yet, but we will do it in the following couple of steps.

Now, we need to add the click event to the Add Product Button. So add the following code inside a product-add.component.html file.

<div class="form-group">
        <button (click) = "addProduct(ProductName.value, ProductDescription.value, ProductPrice.value)" type="submit" class="btn btn-primary"
        [disabled]="angForm.pristine || angForm.invalid" >
          Create Product
        </button>
</div>

So when there are no errors, we can submit the form, and it will call the component’s addPropduct function. From there, we will call the angular service, and the service will send the HTTP Post request to the Node.js server.

Now, add the addProduct function inside the product-add.component.ts file. So write the following code inside the product-add.component.ts file.

// product-add.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ProductsService } from '../products.service';

@Component({
  selector: 'app-product-add',
  templateUrl: './product-add.component.html',
  styleUrls: ['./product-add.component.css']
})
export class ProductAddComponent implements OnInit {

  angForm: FormGroup;
  constructor(private fb: FormBuilder, private ps: ProductsService) {
    this.createForm();
  }

  createForm() {
    this.angForm = this.fb.group({
      ProductName: ['', Validators.required ],
      ProductDescription: ['', Validators.required ],
      ProductPrice: ['', Validators.required ]
    });
  }

  addProduct(ProductName, ProductDescription, ProductPrice) {
    this.ps.addProduct(ProductName, ProductDescription, ProductPrice);
  }

  ngOnInit() {
  }

}

In the above code, first, I have imported the products.service.ts file. Then we have defined the addProduct method.

This method is called when the user clicks on the create product button, and the flow will transfer over here. We get the ProductName, ProductDescription, and ProductPrice data inside the addProduct function.

Inside that function, we are calling the products.service.ts file’s addProduct function and pass the three parameters with it.

So, the products.service.ts file will call the API and save the products on the server. Now, we need to configure the backend API.

Up until now, we have reached half of this Angular 8 CRUD example.

In the next half, we will talk about Node, Express, MongoDB, API, and stuff because this tutorial is an Angular 8  CRUD Full Stack Example.

Step 12: Create a backend API in Node

Inside the angular root folder, create one folder called the API and go inside that folder. Remember, it will be a completely separate project from the Angular frontend project.

So its node_modules folders are different from the Angular project.

Open a terminal inside the API folder and type the following command. It will generate the package.json file using NPM. We do not want to specify each option one by one; we are typing the following command.

npm init -y

Install the following node-specific modules.

npm install express body-parser cors mongoose --save

I do not want to restart the node server each time; I change the file. So I am installing the nodemon server. What it does is that when I modify a server.js file, it restarts a node.js server automatically.

npm install nodemon --save-dev

We are also installing the body-parser module to parse the data from the incoming Http request.

We have installed the CORS module because our both angular and node application is running on the different ports.

The browser will not allow the CROSS REQUEST ORIGIN attack; that is why we need to install the CORS module to receive the proper request at the backend server.

We have installed the Mongoose module because it provides the ORM for the MongoDB database.

Now, inside the API folder, create one file called the server.js file. Add the following code inside that file.

// server.js

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

    const app = express();
    let port = process.env.PORT || 4000;

    const server = app.listen(function(){
        console.log('Listening on port ' + port);
    });

The next thing is to connect the MongoDB database with our node express application.

If you have not installed a MongoDB database, then install it and then start the MongoDB server using the following command.

mongod

So, Now, I have connected to a database.

Create one file called DB.js inside the api root project folder. Write the following code inside a DB.js file.

// DB.js

module.exports = {
  DB: 'mongodb://localhost:27017/ng8crud'
};

Import this DB.js file inside our server.js file and use the Mongoose library to set up the database connection with MongoDB. We can also use Mongoose to save the data in the database using the Mongoose ORM.

Write the following code inside a server.js file to connect our MongoDB application to a Node.js server.

// server.js

const express = require('express'),
    path = require('path'),
    bodyParser = require('body-parser'),
    cors = require('cors'),
    mongoose = require('mongoose'),
    config = require('./DB');

    mongoose.Promise = global.Promise;
    mongoose.connect(config.DB, { useNewUrlParser: true }).then(
      () => {console.log('Database is connected') },
      err => { console.log('Can not connect to the database'+ err)}
    );

    const app = express();
    app.use(bodyParser.json());
    app.use(cors());
    const port = process.env.PORT || 4000;

    const server = app.listen(port, function(){
     console.log('Listening on port ' + port);
    });

Save the above server.js file and go to the terminal and start the node server using the following command. Remember, we are using nodemon.

nodemon server

So, right now, you have three servers running.

  1. Angular Development Server for frontend.
  2. Nodemon server for the backend.
  3. MongoDB server for database.

Remember, all three servers are running fine without any error; otherwise, our application will not work, and it will crash.

Step 13: Create a route and model files.

Now, we need to create two folders inside the api root folder called routes and models.

In the models’ folder, create one model called Product.js.

// Product.js

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

// Define collection and schema for Product
let Product = new Schema({
  ProductName: {
    type: String
  },
  ProductDescription: {
    type: String
  },
  ProductPrice: {
    type: Number
  }
},{
    collection: 'Product'
});

module.exports = mongoose.model('Product', Product);

So, we have defined our schema for the Product collection. We have three fields called ProductName, ProductDescription, ProductPrice.

In the routes folder, create one file called the product.route.js.

Write the CRUD code inside the product.route.js file.

// product.route.js

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

// Require Product model in our routes module
let Product = require('../models/Product');

// Defined store route
productRoutes.route('/add').post(function (req, res) {
  let product = new Product(req.body);
  product.save()
    .then(product => {
      res.status(200).json({'Product': 'Product has been added successfully'});
    })
    .catch(err => {
    res.status(400).send("unable to save to database");
    });
});

// Defined get data(index or listing) route
productRoutes.route('/').get(function (req, res) {
  Product.find(function (err, products){
    if(err){
      console.log(err);
    }
    else {
      res.json(products);
    }
  });
});

// Defined edit route
productRoutes.route('/edit/:id').get(function (req, res) {
  let id = req.params.id;
  Product.findById(id, function (err, product){
      res.json(product);
  });
});

//  Defined update route
productRoutes.route('/update/:id').post(function (req, res) {
  Product.findById(req.params.id, function(err, product) {
    if (!product)
      res.status(404).send("Record not found");
    else {
      product.ProductName = req.body.ProductName;
      product.ProductDescription = req.body.ProductDescription;
      product.ProductPrice = req.body.ProductPrice;

      product.save().then(product => {
          res.json('Update complete');
      })
      .catch(err => {
            res.status(400).send("unable to update the database");
      });
    }
  });
});

// Defined delete | remove | destroy route
productRoutes.route('/delete/:id').get(function (req, res) {
    Product.findByIdAndRemove({_id: req.params.id}, function(err, product){
        if(err) res.json(err);
        else res.json('Successfully removed');
    });
});

module.exports = productRoutes;

Here, we have used the mongoose model to save, update, and delete the database. Mongoose is an ORM used in the MongoDB database. It will handle all the CRUD tasks at the backend. Now, we have all the CRUD operations functions set up on the route file. The final thing is that we need to import inside the server.js file.

Remember, the server.js file is the starting point of our backend node application. So every module needs to be included inside the server.js file.

So, our final server.js file looks like this.

// server.js

const express = require('express'),
    path = require('path'),
    bodyParser = require('body-parser'),
    cors = require('cors'),
    mongoose = require('mongoose'),
    config = require('./DB');

   const productRoute = require('./routes/product.route');
    mongoose.Promise = global.Promise;
    mongoose.connect(config.DB, { useNewUrlParser: true }).then(
      () => {console.log('Database is connected') },
      err => { console.log('Can not connect to the database'+ err)}
    );

    const app = express();
    app.use(bodyParser.json());
    app.use(cors());
    app.use('/products', productRoute);
    const port = process.env.PORT || 4000;

    const server = app.listen(port, function(){
     console.log('Listening on port ' + port);
    });

Now, go to the terminal and start the node server if you have not already started.

node server

#Step 14: Save data in MongoDB database

If all of your servers are up and running, then you can go to the browser and fill the form data and add the Product. You can see something like this on your screen if you are successful.

Sometimes, if you are running an adblocker on the browser, then it will not work. So please turn off the adblocker and try this example again.

Test the store data functionality

Now, we can check on the database using the following commands.

First, open the mongo shell on the 4th tab because all the other three tabs are occupied at the moment.

mongo

MongoDB Add

Here, we can see that the values are storing in the MongoDB database. Yess!! We have succeeded.

Now, the remaining operations are Read, Update, and Delete.

#Step 15: Show the data on Angular Frontend

We can iterate the backend data and display the data in the tabular format using Angular ngfor directive.

Inside the product-get.component.html file, write the following code.

<!-- product-get.component.html -->

<table class="table table-hover">
  <thead>
  <tr>
      <td>Product Name</td>
      <td>Product Description</td>
      <td>Product Price</td>
      <td colspan="2">Actions</td>
  </tr>
  </thead>

  <tbody>
      <tr *ngFor="let product of products">
          <td>{{ product.ProductName }}</td>
          <td>{{ product.ProductDescription }}</td>
          <td>{{ product.ProductPrice }}</td>
          <td><a [routerLink]="['/edit', product._id]" class="btn btn-primary">Edit</a></td>
          <td><a [routerLink]="" class="btn btn-danger">Delete</a></td>
      </tr>
  </tbody>
</table>

Now, inside the products.service.ts file, we need to write the function that fetches the product data from the MongoDB database and display it at the Angular application.

// products.service.ts

getProducts() {
    return this
           .http
           .get(`${this.uri}`);
  }

In the above getProducts() function, we have sent the HTTP GET request to the Node.js server and fetch the data from the database.

Now, we need to include the products.service.ts file and Product.ts file inside a product-get.component.ts file.

Write the following code inside the product-get.component.ts file.

// product-get.component.ts

import { Component, OnInit } from '@angular/core';
import Product from '../Product';
import { ProductsService } from '../products.service';

@Component({
  selector: 'app-product-get',
  templateUrl: './product-get.component.html',
  styleUrls: ['./product-get.component.css']
})
export class ProductGetComponent implements OnInit {

  products: Product[];
  constructor(private ps: ProductsService) { }

  ngOnInit() {
    this.ps
      .getProducts()
      .subscribe((data: Product[]) => {
        this.products = data;
    });
  }

}

Save the file and go to the browser, and switch to this URL: http://localhost:4200/products. You can see the listing of the products.

Display the data on the frontend

 

#Step 16: Edit and update fields

Okay, first, we need to fetch the data from the MongoDB database using _id wise and display that data in the product-edit.component.html file.

When the product-edit.component.html loads, we send an AJAX request to the node server and fetch the particular row using the _id and display the data to their respective fields inside the HTML form.

So first, write the following code inside the product-edit.component.ts file.

// product-edit.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ProductsService } from '../products.service';

@Component({
  selector: 'app-product-edit',
  templateUrl: './product-edit.component.html',
  styleUrls: ['./product-edit.component.css']
})
export class ProductEditComponent implements OnInit {

  angForm: FormGroup;
  product: any = {};

  constructor(private route: ActivatedRoute, private router: Router, private ps: ProductsService, private fb: FormBuilder) {
      this.createForm();
 }

  createForm() {
    this.angForm = this.fb.group({
      ProductName: ['', Validators.required ],
      ProductDescription: ['', Validators.required ],
      ProductPrice: ['', Validators.required ]
    });
  }

  ngOnInit() {
    this.route.params.subscribe(params => {
        this.ps.editProduct(params['id']).subscribe(res => {
          this.product = res;
      });
    });
  }
}

Here, when the product-edit component.ts render, it will call the ngOnInit method and send an HTTP request to the node server and fetch the data from an _id to display inside the product-edit component.html file.

Now, inside the products.service.ts file, we need to code the editProduct function to send an HTTP request.

// products.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ProductsService {

  uri = 'http://localhost:4000/products';

  constructor(private http: HttpClient) { }

  addProduct(ProductName, ProductDescription, ProductPrice) {
    console.log(ProductName, ProductDescription, ProductPrice);
    const obj = {
      ProductName,
      ProductDescription,
      ProductPrice
    };
    this.http.post(`${this.uri}/add`, obj)
        .subscribe(res => console.log('Done'));
  }

  getProducts() {
    return this
           .http
           .get(`${this.uri}`);
  }

  editProduct(id) {
    return this
            .http
            .get(`${this.uri}/edit/${id}`);
    }
}

Now, finally, we need to write the form inside the product-edit.component.html file.

<!-- product-edit.component.html -->

<div class="card">
  <div class="card-body">
    <form [formGroup]="angForm" novalidate>
      <div class="form-group">
        <label class="col-md-4">Product Name</label>
        <input type="text" class="form-control" 
          formControlName="ProductName" 
          #ProductName 
          [(ngModel)] = "product.ProductName"/>
      </div>
      <div *ngIf="angForm.controls['ProductName'].invalid && (angForm.controls['ProductName'].dirty || angForm.controls['ProductName'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductName'].errors.required">
          Product Name is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Description </label>
        <textarea class="form-control" rows = 7 cols = "5"
        formControlName="ProductDescription" 
        #ProductDescription [(ngModel)] = "product.ProductDescription"></textarea>
      </div>
      <div *ngIf="angForm.controls['ProductDescription'].invalid && (angForm.controls['ProductDescription'].dirty || angForm.controls['ProductDescription'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductDescription'].errors.required">
          Product Description is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Product Price</label>
        <input type="text" class="form-control" 
          formControlName="ProductPrice" 
          #ProductPrice
          [(ngModel)] = "product.ProductPrice"
        />
      </div>
      <div *ngIf="angForm.controls['ProductPrice'].invalid && (angForm.controls['ProductPrice'].dirty || angForm.controls['ProductPrice'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['ProductPrice'].errors.required">
          Product Price is required.
        </div>
      </div>
      <div class="form-group">
        <button (click) = "updateProduct(ProductName.value, ProductDescription.value, ProductPrice.value)" type="submit" class="btn btn-primary"
        [disabled]="angForm.invalid" >
          Update Product
        </button>
      </div>
    </form>
  </div>
</div>

Save the file and go to the listing page and click on the edit button and you will see the populated form fields from the database.

You can also see the warning like the following. Ignore this demo tutorial.

forms.js:1193
It looks like you’re using ngModel on the same form field as formControlName.
Support for using the ngModel input property and ngModelChange event with
reactive form directives have been deprecated in Angular v6 and will be removed
in Angular v7.
Now, we need to update the data into the database.
Inside the products.service.ts file, we need to write a function that updates the form fields data. See the following code.
// products.service.ts

updateProduct(ProductName, ProductDescription, ProductPrice, id) {
    const obj = {
      ProductName,
      ProductDescription,
      ProductPrice
    };
    this
      .http
      .post(`${this.uri}/update/${id}`, obj)
      .subscribe(res => console.log('Done'));
}

Okay, now write the updateProduct() function inside product-edit.component.ts file.

// product-edit.component.ts

updateProduct(ProductName, ProductDescription, ProductPrice, id) {
    this.route.params.subscribe(params => {
      this.ps.updateProduct(ProductName, ProductDescription, ProductPrice, params.id);
      this.router.navigate(['products']);
    });
  }

In the above function, we get the form values from the HTML forms and sends the PUT request to the node server with the updated values, and at the backend, the update function will call and update the values inside the MongoDB database.

Save the file, and you will be able to update the data.

#Step 17: Delete form data.

So, if you find no error on the console, you can successfully update the form data.

I have already written an edit and update service to make API calls. So till now, we have completed the Create, Read, Update task of this Angular 8 Tutorial with CRUD Example post. Now, take a look at Delete or remove the data from the database.

Now, we need to define the click event on the delete button inside the product-get.component.html file.

<!-- product-get.component.html -->

<tbody>
      <tr *ngFor="let product of products">
          <td>{{ product.ProductName }}</td>
          <td>{{ product.ProductDescription }}</td>
          <td>{{ product.ProductPrice }}</td>
          <td><a [routerLink]="['/edit', product._id]" class="btn btn-primary">Edit</a></td>
          <td><a (click) = "deleteProduct(product._id)" class="btn btn-danger">Delete</a>
      </tr>
  </tbody>

Now, write the deleteProduct function inside the product-get.component.ts file.

// product-get.component.ts

deleteProduct(id) {
    this.ps.deleteProduct(id).subscribe(res => {
      this.products.splice(id, 1);
    });
}

The above function will send the ID to the server to delete the row from the database, and on the frontend, we are using the Javascript Splice function to remove the data from the Angular application.

Now, create deleteProduct() function inside the product.service.ts file. We will send an ID in this AJAx request to delete the data from the backend.

// products.service.ts

deleteProduct(id) {
    return this
              .http
              .get(`${this.uri}/delete/${id}`);
  }

Finally, I completed the delete data or remove data functionality.

So, in this tutorial, we have completed the CRUD Functionality in Angular 8. I have also put the code on Github.

If you have any doubt about this Angular 8 Tutorial With CRUD Example, ask in a comment below. If I were busy, then some will surely reply to you.

GITHUB CODE

Recommended Posts

Angular 8 File Upload

Angular 8 HttpClient

Angular 8 Updates And Summary

How To Update Angular CLI To Version 8

Angular 8 Forms

Angular NgFor

Angular NgStyle

Angular Modal

Angular NgClass

Angular Drag and Drop

67 thoughts on “Angular 8 CRUD: Angular 8 Tutorial For Beginners”

  1. Hi krunal,

    Thank you very much for this first.

    There is one thing which i found, when we update the record it is getting updating in database but not getting reflecting get products view however it reflects when we refresh the get products view.
    As I am seeing yoy have use this.router.navigate([‘products’]); in updateProduct method. is any additional configuration need to set to get immediate refection on viewproducts page as soon as we update the record.

    Thanks
    N s Patil

    Reply
  2. You need to navigate when the update is done.

    Change the http.post in the updateProduct function in products.service.ts for this:

    this.http.post(`${this.uri}/update/${id}`, obj).subscribe(res => this.router.navigate([‘products’]));

    This is a quick way to fix this.

    Reply
  3. Thanks very impressive Tutorial.
    What happened to the ClientApp folder, Is that only used in Visual Studio ASP.NET projects?

    Reply
  4. Very nice tutorial, but how can we ignore that warning when you are doing a tutorial for Angular 8. Do you have an editing solution without mixing the [(ngModel)] with Reactive Forms?

    Reply
  5. i followed your angular 7 CRUD and there was the same warning. i thought this time you rectify this, but you did not. why?

    Reply
  6. After completing this tutorial, I tried to run ng build –prod and got this:

    ERROR in srcappproduct-editproduct-edit.component.html (41,17) expected 4 arguments, but got 3

    Reply
  7. hi great tutorial, Im following, but, unfortunely, when I try to start the nodemon server to start to the next step for connect to the mongoDb , the terminal show me this error

    [nodemon] 1.19.1
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching: *.*
    [nodemon] starting `ng serve server`
    An unhandled exception occurred: Project ‘server’ does not support the ‘serve’ target.
    See “D:\Users\FANAZC~1.FAR\AppData\Local\Temp\ng-B48UQn\angular-errors.log” for further details.
    [nodemon] failed to start process, “ng serve” exec not found
    [nodemon] Error

    can you help me to solve this, in google I didn´t find info

    Reply
    • Hi @Jesus Says

      You must run the nodemon server inside the API path in cmd prompt.
      like this

      C:\Users\MEAN\store\api>nodemon server
      [nodemon] 1.19.1
      [nodemon] to restart at any time, enter `rs`
      [nodemon] watching: *.*
      [nodemon] starting `node server.js`
      Listening on port 4000
      Database is connected

      Reply
      • LOL yeah , I realized of that mistake thanks a lot, today finally ended the crud (thanks to my job I didn´t) best regards

        Reply
  8. zone-evergreen.js:2952 POST http://localhost:4000/products/add 400 (Bad Request)
    scheduleTask @ zone-evergreen.js:2952
    scheduleTask @ zone-evergreen.js:378
    onScheduleTask @ zone-evergreen.js:272
    scheduleTask @ zone-evergreen.js:372
    scheduleTask @ zone-evergreen.js:211
    scheduleMacroTask @ zone-evergreen.js:234
    scheduleMacroTaskWithCurrentZone @ zone-evergreen.js:1107
    (anonymous) @ zone-evergreen.js:2985
    proto. @ zone-evergreen.js:1428
    (anonymous) @ http.js:2065
    _trySubscribe @ Observable.js:42
    subscribe @ Observable.js:28
    (anonymous) @ subscribeTo.js:20
    subscribeToResult @ subscribeToResult.js:7
    _innerSub @ mergeMap.js:59
    _tryNext @ mergeMap.js:53
    _next @ mergeMap.js:36
    next @ Subscriber.js:49
    (anonymous) @ scalar.js:4
    _trySubscribe @ Observable.js:42
    subscribe @ Observable.js:28
    call @ mergeMap.js:21
    subscribe @ Observable.js:23
    call @ filter.js:13
    subscribe @ Observable.js:23
    call @ map.js:16
    subscribe @ Observable.js:23
    addProduct @ products.service.ts:21
    addProduct @ product-add.component.ts:26
    (anonymous) @ ProductAddComponent.html:39
    handleEvent @ core.js:34789
    callWithDebugContext @ core.js:36407
    debugHandleEvent @ core.js:36043
    dispatchEvent @ core.js:22533
    (anonymous) @ core.js:33721
    (anonymous) @ platform-browser.js:1789
    invokeTask @ zone-evergreen.js:391
    onInvokeTask @ core.js:30885
    invokeTask @ zone-evergreen.js:390
    runTask @ zone-evergreen.js:168
    invokeTask @ zone-evergreen.js:465
    invokeTask @ zone-evergreen.js:1603
    globalZoneAwareCallback @ zone-evergreen.js:1629
    Show 13 more frames
    core.js:7187 ERROR HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: “Bad Request”, url: “http://localhost:4000/products/add”, ok: false, …}
    defaultErrorLogger @ core.js:7187
    handleError @ core.js:7239
    next @ core.js:31614
    schedulerFn @ core.js:27846
    __tryOrUnsub @ Subscriber.js:185
    next @ Subscriber.js:124
    _next @ Subscriber.js:72
    next @ Subscriber.js:49
    next @ Subject.js:39
    emit @ core.js:27808
    (anonymous) @ core.js:30943
    invoke @ zone-evergreen.js:359
    run @ zone-evergreen.js:124
    runOutsideAngular @ core.js:30830
    onHandleError @ core.js:30940
    handleError @ zone-evergreen.js:363
    runTask @ zone-evergreen.js:171
    invokeTask @ zone-evergreen.js:465
    ZoneTask.invoke @ zone-evergreen.js:454
    timer @ zone-evergreen.js:2650
    setTimeout (async)
    scheduleTask @ zone-evergreen.js:2671
    scheduleTask @ zone-evergreen.js:378
    onScheduleTask @ zone-evergreen.js:272
    scheduleTask @ zone-evergreen.js:372
    scheduleTask @ zone-evergreen.js:211
    scheduleMacroTask @ zone-evergreen.js:234
    scheduleMacroTaskWithCurrentZone @ zone-evergreen.js:1107
    (anonymous) @ zone-evergreen.js:2686
    proto. @ zone-evergreen.js:1428
    hostReportError @ hostReportError.js:2
    error @ Subscriber.js:158
    _error @ Subscriber.js:75
    error @ Subscriber.js:55
    _error @ Subscriber.js:75
    error @ Subscriber.js:55
    _error @ Subscriber.js:75
    error @ Subscriber.js:55
    notifyError @ OuterSubscriber.js:7
    _error @ InnerSubscriber.js:14
    error @ Subscriber.js:55
    onLoad @ http.js:1961
    invokeTask @ zone-evergreen.js:391
    onInvokeTask @ core.js:30885
    invokeTask @ zone-evergreen.js:390
    runTask @ zone-evergreen.js:168
    invokeTask @ zone-evergreen.js:465
    invokeTask @ zone-evergreen.js:1603
    globalZoneAwareCallback @ zone-evergreen.js:1640
    load (async)
    customScheduleGlobal @ zone-evergreen.js:1742
    scheduleTask @ zone-evergreen.js:378
    onScheduleTask @ zone-evergreen.js:272
    scheduleTask @ zone-evergreen.js:372
    scheduleTask @ zone-evergreen.js:211
    scheduleEventTask @ zone-evergreen.js:237
    (anonymous) @ zone-evergreen.js:1911
    (anonymous) @ http.js:2053
    _trySubscribe @ Observable.js:42
    subscribe @ Observable.js:28
    (anonymous) @ subscribeTo.js:20
    subscribeToResult @ subscribeToResult.js:7
    _innerSub @ mergeMap.js:59
    _tryNext @ mergeMap.js:53
    _next @ mergeMap.js:36
    next @ Subscriber.js:49
    (anonymous) @ scalar.js:4
    _trySubscribe @ Observable.js:42
    subscribe @ Observable.js:28
    call @ mergeMap.js:21
    subscribe @ Observable.js:23
    call @ filter.js:13
    subscribe @ Observable.js:23
    call @ map.js:16
    subscribe @ Observable.js:23
    addProduct @ products.service.ts:21
    addProduct @ product-add.component.ts:26
    (anonymous) @ ProductAddComponent.html:39
    handleEvent @ core.js:34789
    callWithDebugContext @ core.js:36407
    debugHandleEvent @ core.js:36043
    dispatchEvent @ core.js:22533
    (anonymous) @ core.js:33721
    (anonymous) @ platform-browser.js:1789
    invokeTask @ zone-evergreen.js:391
    onInvokeTask @ core.js:30885
    invokeTask @ zone-evergreen.js:390
    runTask @ zone-evergreen.js:168
    invokeTask @ zone-evergreen.js:465
    invokeTask @ zone-evergreen.js:1603
    globalZoneAwareCallback @ zone-evergreen.js:1629
    Show 41 more frames
    products.service.ts:19 {ProductName: “jbkj”, ProductDescription: “hfh”, ProductPrice: “yu”}
    :4000/products/add:1 POST http://localhost:4000/products/add 400 (Bad Request)
    core.js:7187 ERROR HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: “Bad Request”, url: “http://localhost:4000/products/add”, ok: false, …}

    Reply
  9. Hi @Jesus Says

    You must run the nodemon server inside the API path in cmd prompt.
    like this

    C:\Users\MEAN\store\api>nodemon server
    [nodemon] 1.19.1
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching: *.*
    [nodemon] starting `node server.js`
    Listening on port 4000
    Database is connected

    Reply
  10. ERROR Error: “Uncaught (in promise): Error: Cannot match any routes. URL Segment: ‘products/edit/5d4948982897c040147caed3’
    noMatchError@http://localhost:4200/vendor.js:93863:16
    apply/<@http://localhost:4200/vendor.js:93827:28
    error@http://localhost:4200/vendor.js:104256:31
    _error@http://localhost:4200/vendor.js:101306:26
    error@http://localhost:4200/vendor.js:101286:18
    _error@http://localhost:4200/vendor.js:101306:26
    error@http://localhost:4200/vendor.js:101286:18
    _error@http://localhost:4200/vendor.js:101306:26
    error@http://localhost:4200/vendor.js:101286:18
    ………………………….
    Any idea ?

    Reply
  11. Hi krunal,

    Thank you very much for this great tutorial.
    I have a question:
    I tried to insert a inside the form but when I try to take the value of the select the console.log prints “undefined”

    This is my html

    Order

    Select
    One
    Two
    Three

    I think the error is here.
    (In the future I will replace the options with an ngfor)

    Where am I wrong?
    Can you give an example also for the ?

    Thanks a lot.

    Reply
  12. Awesome

    The Tutorial is really complete and extraordinarily explained and it is easy to understand, even for new users of angular.

    Also bring us to mongodb that is a incredible NOSQL Database and sure the Node.js (Awesome).

    Reply
  13. In step 12 nodemon should be installed globally otherwise when you start it you will get an error stating:
    -bash: nodemon: command not found

    Try

    sudo npm install -g nodemon –save-dev

    Reply
  14. It worked already thx

    If i want to use more than 1 schema how can i do it? Let’s say i want a login page and create accounts,do i need another script like server.js just for login routes?

    Reply
    • You need to define all of your routes let’s say in routes folder and import all the routes module inside the server.js file. There is only one server.js file for every project. You just need to import the different routing module inside the server.js file and you are good to go.

      Reply
  15. Hey, I’m trying to get search to work, but the query string keeps returning all the values from the database.
    How do I filter the values from the querystring?

    Reply
  16. DELETE is not working properly. In product-get.component.html you give to the
    deleteProduct function product._id which is not an id it’s just a hash.
    so why would the splice in product-get.component.ts in deleteProduct function work?
    I removed the splice and it works.

    Reply
  17. [Error] Failed to load resource: the server responded with a status of 404 (Not Found) (add, line 0)

    And it happens for add, edit, like the folders and files wasn’t there. I think it could be writing on db forbidden on my Mac or uri problems on code, may be….

    Reply
  18. I am getting below error in the debugger console when I try to add

    Object { headers: {…}, status: 400, statusText: “Bad Request”, url: “http://localhost:4000/products/add”, ok: false, name: “HttpErrorResponse”, message: “Http failure response for http://localhost:4000/products/add: 400 Bad Request”, error: “unable to save to database” }

    Reply
    • Hi Krunal and all the community here.
      I want to thank you for this excellent tutorial and all the help provided here. It has been very helpful for me.

      Henry

      Reply
  19. Great tutorial !! Simply awesome !! Step by step guide for anybody starting to learn server side java script programming. I will definitely encourage anybody to go through this tutorial.

    Cheers !
    Pal

    Reply
  20. Very good article Krunal for Angular8.
    Just one change done : added “@import “../node_modules/bootstrap/dist/css/bootstrap.css”; in style.css to apply the css. rest is fine.

    Reply
  21. Where i need run this cmd inside API root folder or Angular root folder or globally in cmd

    The next thing is to connect the MongoDB database with our node express application.

    If you have not installed a MongoDB database, then install it and then start the MongoDB server using the following command.

    mongod

    Reply
      • Krunal, can i tried it in root and in api folder but it shows error.
        mongo : The term ‘mongo’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
        of the name, or if a path was included, verify that the path is correct and try again.
        At line:1 char:1
        + mongo
        + ~~~~~
        + CategoryInfo : ObjectNotFound: (mongo:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

        I’m using visualstudio code. can you help me about this?

        Reply
  22. I am new to Angular. And my primary interest is to follow the development of CRUD example using MongoDB. I have found this article very useful in learning the technology. But I am facing a problem. I don’t have any errors. All the servers – Mongo, Express and ng – are running fine. But when I click on the Create Product button nothing happens, that is, the mongo db is not updated.

    Can any one help?

    Reply
  23. Fantastic tutorial! Two notes for anybody out there who might be puzzled.

    In Step 16, the updateProduct method on the product-edit.component.ts accepts 4 arguments, but should only accept 3. Remove the id and it will still work because the id passed to the service doesn’t come from outside the method. It comes from the route parameters defined within the method itself.

    In Step 17, the deleteProduct method always removes the first item in the list on the template. To fix, pass the index for the product in the array from the template to the method (like this: https://stackoverflow.com/questions/44612400/how-to-pass-the-index-value-of-a-ngfor-loop-into-the-component), and use it as the first argument in the splice method instead of the id. The ID isn’t the correct value to pass to the splice method to remove the deleted item from the array, you must pass the index value.

    Reply
  24. Hi, Kunal
    It’s a fantastic tutorial, Thanks for this.
    But facing one error, help to resolve it
    ERROR in src/app/product-edit/product-edit.component.html:43:30 – error TS2554: Expected 4 arguments, but got 3.

    43 <button (click) = "updateProduct(ProductName.value, ProductDescription.value, ProductPrice.value)" type="submit" class="btn btn-primary"

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.