Angular 8 CRUD Application example with Web API

In this tutorial, I will show you how to build an Angular 8 CRUD Application to consume Web APIs, display, modify & search data.

Newer versions:
Angular 10 CRUD example with Web API
Angular 11 CRUD example with Web API
Angular 12 CRUD example with Web API
Angular 13 CRUD example with Web API
Angular 14 CRUD example with Web API
Angular 15 CRUD example with Web API

More Practice:
Angular 8 JWT Authentication with HttpInterceptor and Router
Angular 8 Multiple Files upload example
Angular 8 + Spring Boot: File upload example

Fullstack CRUD Application:
Angular 8 + Node.js Express + MySQL example
Angular 8 + Node.js Express + PostgreSQL example
Angular 8 + Node.js Express + MongoDB example
Angular 8 + Spring Boot + MySQL example
Angular 8 + Spring Boot + PostgreSQL example
Angular 8 + Spring Boot + MongoDB example
Angular 8 + Django example
Angular 8 + Django + MySQL example
Angular 8 + Django + PostgreSQL example
Angular 8 + Django + MongoDB example

Serverless with Firebase:
Angular 8 Firebase CRUD Realtime DB | AngularFireDatabase

Overview of Angular 8 CRUD Application

We will build an Angular 8 front-end Tutorial Application in that:

  • Each Tutorial has id, title, description, published status.
  • We can create, retrieve, update, delete Tutorials.
  • There is a Search bar for finding Tutorials by title.

Here are screenshots of our Angular CRUD Application.

– Create a Tutorial:

angular-crud-app-create

– Retrieve all Tutorials:

angular-crud-app-retrieve-all

– Click on Edit button to update a Tutorial:

angular-crud-app-update

On this Page, you can:

  • change status to Published using Publish button
  • delete the Tutorial using Delete button
  • update the Tutorial details with Update button

– Search Tutorials by title:

angular-crud-app-search

Web API

The introduction above is for Angular Client with assumption that we have a Server exporting REST APIs:

Methods Urls Actions
POST /api/tutorials create new Tutorial
GET /api/tutorials retrieve all Tutorials
GET /api/tutorials/:id retrieve a Tutorial by :id
PUT /api/tutorials/:id update a Tutorial by :id
DELETE /api/tutorials/:id delete a Tutorial by :id
DELETE /api/tutorials delete all Tutorials
GET /api/tutorials?title=[keyword] find all Tutorials which title contains keyword

You can find step by step to build a Server like this in one of these posts:
Express, Sequelize & MySQL
Express, Sequelize & PostgreSQL
Express, Sequelize & SQL Server
Express & MongoDb
Spring Boot & MySQL
Spring Boot & PostgreSQL
Spring Boot & MongoDB
Spring Boot & SQL Server
Spring Boot & H2
Spring Boot & Cassandra
Spring Boot & Oracle
Python/Django & MySQL
Python/Django & PostgreSQL
Python/Django & MongoDB

All of them can work well with this Angular App.

Angular App Component Diagram

angular-crud-app-components-diagram

– The App component is a container with router-outlet. It has navbar that links to routes paths via routerLink.

TutorialsList component gets and displays Tutorials.
Tutorial component has form for editing Tutorial’s details based on :id.
AddTutorial component has form for submission new Tutorial.

– These Components call TutorialService methods which use Angular HTTPClient to make HTTP requests and receive responses.

Setup Angular 8 Project

Let’s open cmd and use Angular CLI to create a new Angular Project as following command:

ng new Angular8ClientCrud
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS

We also need to generate some Components and Services:

ng g s services/tutorial

ng g c components/add-tutorial
ng g c components/tutorial-details
ng g c components/tutorials-list

Now you can see that our project directory structure looks like this.

Project Structure

angular-crud-app-project-structure

Let me explain it briefly.

– There are 3 components: tutorials-list, tutorial-details, add-tutorial.
tutorial.service has methods for sending HTTP requests to the Apis.
app-routing.module.ts defines routes for each component.
app component contains router view and navigation bar.
app.module.ts declares Angular components and import necessary modules.

Set up App Module

Open app.module.ts and import FormsModule, HttpClientModule:

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

@NgModule({
  declarations: [ ... ],
  imports: [
    ...
    FormsModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Define Routes for Angular AppRoutingModule

There are 3 main routes:
/tutorials for tutorials-list component
/tutorials/:id for tutorial-details component
/add for add-tutorial component

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';

const routes: Routes = [
  { path: '', redirectTo: 'tutorials', pathMatch: 'full' },
  { path: 'tutorials', component: TutorialsListComponent },
  { path: 'tutorials/:id', component: TutorialDetailsComponent },
  { path: 'add', component: AddTutorialComponent }
];

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

Add Navbar and Router View to Angular CRUD App

Let’s open src/app.component.html, this App component is the root container for our application, it will contain a nav element.

<div>
  <nav class="navbar navbar-expand navbar-dark bg-dark">
    <a href="#" class="navbar-brand">bezKoder</a>
    <div class="navbar-nav mr-auto">
      <li class="nav-item">
        <a routerLink="tutorials" class="nav-link">Tutorials</a>
      </li>
      <li class="nav-item">
        <a routerLink="add" class="nav-link">Add</a>
      </li>
    </div>
  </nav>

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

Create Data Service

This service will use Angular HTTPClient to send HTTP requests.
You can see that its functions includes CRUD operations and finder method.

services/tutorial.service.ts

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

const baseUrl = 'http://localhost:8080/api/tutorials';

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

  constructor(private http: HttpClient) { }

  getAll() {
    return this.http.get(baseUrl);
  }

  get(id) {
    return this.http.get(`${baseUrl}/${id}`);
  }

  create(data) {
    return this.http.post(baseUrl, data);
  }

  update(id, data) {
    return this.http.put(`${baseUrl}/${id}`, data);
  }

  delete(id) {
    return this.http.delete(`${baseUrl}/${id}`);
  }

  deleteAll() {
    return this.http.delete(baseUrl);
  }

  findByTitle(title) {
    return this.http.get(`${baseUrl}?title=${title}`);
  }
}

Create Angular Components

As you’ve known before, there are 3 components corresponding to 3 routes defined in AppRoutingModule.

Add new Item Component

This component has a Form to submit new Tutorial with 2 fields: title & description. It calls TutorialService.create() method.

components/add-tutorial/add-tutorial.component.ts

import { Component, OnInit } from '@angular/core';
import { TutorialService } from 'src/app/services/tutorial.service';

@Component({
  selector: 'app-add-tutorial',
  templateUrl: './add-tutorial.component.html',
  styleUrls: ['./add-tutorial.component.css']
})
export class AddTutorialComponent implements OnInit {
  tutorial = {
    title: '',
    description: '',
    published: false
  };
  submitted = false;

  constructor(private tutorialService: TutorialService) { }

  ngOnInit() {
  }

  saveTutorial() {
    const data = {
      title: this.tutorial.title,
      description: this.tutorial.description
    };

    this.tutorialService.create(data)
      .subscribe(
        response => {
          console.log(response);
          this.submitted = true;
        },
        error => {
          console.log(error);
        });
  }

  newTutorial() {
    this.submitted = false;
    this.tutorial = {
      title: '',
      description: '',
      published: false
    };
  }
}

components/add-tutorial/add-tutorial.component.html

<div class="submit-form">
  <div *ngIf="!submitted">
    <div class="form-group">
      <label for="title">Title</label>
      <input
        type="text"
        class="form-control"
        id="title"
        required
        [(ngModel)]="tutorial.title"
        name="title"
      />
    </div>

    <div class="form-group">
      <label for="description">Description</label>
      <input
        class="form-control"
        id="description"
        required
        [(ngModel)]="tutorial.description"
        name="description"
      />
    </div>

    <button (click)="saveTutorial()" class="btn btn-success">Submit</button>
  </div>

  <div *ngIf="submitted">
    <h4>You submitted successfully!</h4>
    <button class="btn btn-success" (click)="newTutorial()">Add</button>
  </div>
</div>

List of items Component

This component calls 3 TutorialService methods:

  • getAll()
  • deleteAll()
  • findByTitle()

components/tutorials-list/tutorials-list.component.ts

import { Component, OnInit } from '@angular/core';
import { TutorialService } from 'src/app/services/tutorial.service';

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

  tutorials: any;
  currentTutorial = null;
  currentIndex = -1;
  title = '';

  constructor(private tutorialService: TutorialService) { }

  ngOnInit() {
    this.retrieveTutorials();
  }

  retrieveTutorials() {
    this.tutorialService.getAll()
      .subscribe(
        data => {
          this.tutorials = data;
          console.log(data);
        },
        error => {
          console.log(error);
        });
  }

  refreshList() {
    this.retrieveTutorials();
    this.currentTutorial = null;
    this.currentIndex = -1;
  }

  setActiveTutorial(tutorial, index) {
    this.currentTutorial = tutorial;
    this.currentIndex = index;
  }

  removeAllTutorials() {
    this.tutorialService.deleteAll()
      .subscribe(
        response => {
          console.log(response);
          this.retrieveTutorials();
        },
        error => {
          console.log(error);
        });
  }

  searchTitle() {
    this.tutorialService.findByTitle(this.title)
      .subscribe(
        data => {
          this.tutorials = data;
          console.log(data);
        },
        error => {
          console.log(error);
        });
  }
}

components/tutorials-list/tutorials-list.component.html

<div class="list row">
  <div class="col-md-8">
    <div class="input-group mb-3">
      <input
        type="text"
        class="form-control"
        placeholder="Search by title"
        [(ngModel)]="title"
      />
      <div class="input-group-append">
        <button
          class="btn btn-outline-secondary"
          type="button"
          (click)="searchTitle()"
        >
          Search
        </button>
      </div>
    </div>
  </div>
  <div class="col-md-6">
    <h4>Tutorials List</h4>
    <ul class="list-group">
      <li
        class="list-group-item"
        *ngFor="let tutorial of tutorials; let i = index"
        [class.active]="i == currentIndex"
        (click)="setActiveTutorial(tutorial, i)"
      >
        {{ tutorial.title }}
      </li>
    </ul>

    <button class="m-3 btn btn-sm btn-danger" (click)="removeAllTutorials()">
      Remove All
    </button>
  </div>
  <div class="col-md-6">
    <div *ngIf="currentTutorial">
      <h4>Tutorial</h4>
      <div>
        <label><strong>Title:</strong></label> {{ currentTutorial.title }}
      </div>
      <div>
        <label><strong>Description:</strong></label>
        {{ currentTutorial.description }}
      </div>
      <div>
        <label><strong>Status:</strong></label>
        {{ currentTutorial.published ? "Published" : "Pending" }}
      </div>

      <a class="badge badge-warning" href="/tutorials/{{ currentTutorial.id }}">
        Edit
      </a>
    </div>

    <div *ngIf="!currentTutorial">
      <br />
      <p>Please click on a Tutorial...</p>
    </div>
  </div>
</div>

If you click on Edit button of any Tutorial, You will be directed to Tutorial page with url: /tutorials/:id.

You can add Pagination to this Component, just follow instruction in the post:
Angular 8 Pagination with ngx-pagination example

Item details Component

For getting data & update, delete the Tutorial, this component will use 3 TutorialService methods:

  • get()
  • update()
  • delete()

components/tutorial-details/tutorial-details.component.ts

import { Component, OnInit } from '@angular/core';
import { TutorialService } from 'src/app/services/tutorial.service';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-tutorial-details',
  templateUrl: './tutorial-details.component.html',
  styleUrls: ['./tutorial-details.component.css']
})
export class TutorialDetailsComponent implements OnInit {
  currentTutorial = null;
  message = '';

  constructor(
    private tutorialService: TutorialService,
    private route: ActivatedRoute,
    private router: Router) { }

  ngOnInit() {
    this.message = '';
    this.getTutorial(this.route.snapshot.paramMap.get('id'));
  }

  getTutorial(id) {
    this.tutorialService.get(id)
      .subscribe(
        data => {
          this.currentTutorial = data;
          console.log(data);
        },
        error => {
          console.log(error);
        });
  }

  updatePublished(status) {
    const data = {
      title: this.currentTutorial.title,
      description: this.currentTutorial.description,
      published: status
    };

    this.tutorialService.update(this.currentTutorial.id, data)
      .subscribe(
        response => {
          this.currentTutorial.published = status;
          console.log(response);
        },
        error => {
          console.log(error);
        });
  }

  updateTutorial() {
    this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
      .subscribe(
        response => {
          console.log(response);
          this.message = 'The tutorial was updated successfully!';
        },
        error => {
          console.log(error);
        });
  }

  deleteTutorial() {
    this.tutorialService.delete(this.currentTutorial.id)
      .subscribe(
        response => {
          console.log(response);
          this.router.navigate(['/tutorials']);
        },
        error => {
          console.log(error);
        });
  }
}

components/tutorial-details/tutorial-details.component.html

<div *ngIf="currentTutorial" class="edit-form">
  <h4>Tutorial</h4>
  <form>
    <div class="form-group">
      <label for="title">Title</label>
      <input
        type="text"
        class="form-control"
        id="title"
        [(ngModel)]="currentTutorial.title"
        name="title"
      />
    </div>
    <div class="form-group">
      <label for="description">Description</label>
      <input
        type="text"
        class="form-control"
        id="description"
        [(ngModel)]="currentTutorial.description"
        name="description"
      />
    </div>

    <div class="form-group">
      <label><strong>Status:</strong></label>
      {{ currentTutorial.published ? "Published" : "Pending" }}
    </div>
  </form>

  <button
    class="badge badge-primary mr-2"
    *ngIf="currentTutorial.published"
    (click)="updatePublished(false)"
  >
    UnPublish
  </button>
  <button
    *ngIf="!currentTutorial.published"
    class="badge badge-primary mr-2"
    (click)="updatePublished(true)"
  >
    Publish
  </button>

  <button class="badge badge-danger mr-2" (click)="deleteTutorial()">
    Delete
  </button>

  <button type="submit" class="badge badge-success" (click)="updateTutorial()">
    Update
  </button>
  <p>{{ message }}</p>
</div>

<div *ngIf="!currentTutorial">
  <br />
  <p>Cannot access this Tutorial...</p>
</div>

Run the Angular App

You can run this App with command: ng serve.

If you use this front-end app for one of these back-end Rest APIs:
Express, Sequelize & MySQL
Express, Sequelize & PostgreSQL
Express, Sequelize & SQL Server
Express & MongoDb
Spring Boot & MySQL
Spring Boot & PostgreSQL
Spring Boot & MongoDB
Spring Boot & SQL Server
Spring Boot & H2
Spring Boot & Cassandra
Spring Boot & Oracle
Python/Django & MySQL
Python/Django & PostgreSQL
Python/Django & MongoDB

It configures CORS for port 8081, so you have to run command: ng serve --port 8081 instead.

This is the demo video with brief instruction:

Conclusion

Today we’ve built an Angular 8 CRUD Application successfully working with Web API. Now we can, display, modify, delete or search data in a clean way. I hope you apply it in your project at ease.

You can find newer versions of this Angular App at the posts:
Angular 10 CRUD Application example with Web API
Angular 11 CRUD Application example with Web API
Angular 12 CRUD Application example with Web API
Angular 13 CRUD Application example with Web API

Or you can add Pagination Component:
Angular 8 Pagination with ngx-pagination example

ngx-pagination-angular-8-default-paging

Happy learning, see you again!

Further Reading

Fullstack CRUD Application:

Integration:
Integrate Angular 8 with Node.js Restful Services
Integrate Angular with Spring Boot Rest API

Serverless with Firebase:
Angular 8 Firebase CRUD Realtime DB | AngularFireDatabase

Source Code

You can find the complete source code for this tutorial on Github.

37 thoughts to “Angular 8 CRUD Application example with Web API”

  1. Hi Sir,
    I am able to run this code successfully in Windows machine. When i tried to run the code in mac machine data not showing in UI. Can you please help on this issue.

  2. hi thanks for the article, but what if i want to do it with out an API but hard code the item directly how can i do that ?

  3. The log gives:

    Access to XMLHttpRequest at ‘http://localhost:8080/api/tutorials’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

  4. Hello sir, thank you for your detailed tutorial. I got all the code(back+front) from github and ran both. They ran fine without an error, but I cannot add any tutorial(clicking SUBMIT gives nothing). Can you please help? Thank you.

  5. hello, sir, thanks so much for your tutorial, can I ask you a question? i got a problem in tutorial-list.ts, my Angular shows there is no existing tutorials, i might change tutorials to tutorial in this.tutorials.data. meanwhile, current.Index still has the same issue which can not find in tutorial-list.component.ts. cheers!

    1. i have worked them out, thanks!! i made a mistake. BTW,this is working, but tutorials list can not be shown on the page, and once i add tutorial, there is still no response on the page. Sorry about my question, i am new to Angular

    1. Yes, you can do it by import AddTutorial and TutorialDetails as child components into TutorialsList component.

  6. Thanks for sharing it.

    You have explained every detail. When it comes to building a Crud app with Angular, there are essentials that Angular web developer practices. The first thing to do is to deal with a mountain of data. This data then that lives on the server is dealt with using HTTP operations. In a real app, the data is stored on the server and received via the API.

  7. Hi, this is Alok bharti fron India i am facing some error all are run perfect but backend data is not on web page.

    1. Hi, you should use one of the backend servers that I provided in the tutorials 🙂

  8. Hi!
    i’m facing a probleme with tutorials-list.component.ts when i write this.retrieveTutorials();
    – error TS2339: Property ‘retrieveTutorial’ does not exist ;

  9. How do you run this frontend with the backend api of Mongoose, Express and NodeJS that youve mentioned in another post? theyre in two separate folders names backend and frontend.

  10. Hi there, thanks for this tutorial!
    However, I’ve encountered an issue when i tried to search a tutorial i’ve just created, can you please help me?

    Ps: I’m working on the github version of this app

  11. i did every step correctly, but there is an error with every code that contains “[(ngModel)]=”
    it says “Can’t bind to ‘ngModel’ since it isn’t a known property of ‘input’.ng(0)”
    what might be the problem?

      1. Hi, I’m not the OP, but same problem.

        Yes, I did import FormsModule in app.module.ts. Still have OP error.

        If it helps, VS Code warns “‘FormsModule’ is declared but its value is never read. ts(6133)” HttpClientModule has the same warning, in case that matters.

        Thanks for all your work on these. I’m just starting to learn JS/TS/Angular, and your tutorials have been great so far.

        1. Hi, you should use them in the imports (the tutorial said it clearly):

          ...
          import { FormsModule } from '@angular/forms';
          import { HttpClientModule } from '@angular/common/http';
          
          @NgModule({
            imports: [
              ...
              FormsModule,
              HttpClientModule
            ],
            ...
          })
          export class AppModule { }
          
          1. Thanks for the quick reply.

            Of course, you’re right. The tutorial did say it clearly. I just missed it, even after going back and re-reading. A lot of detail and structure for the two-volts between my ears.

            Great tutorial, and again, thank you.

      2. Had same error, in order to fix it, I had to add the new components to the declarations

        @NgModule({
          declarations: [
            AppComponent,
            AddTutorialComponent,
            TutorialsListComponent,
            TutorialDetailsComponent
        
          ],
        
        1. Ah yeah, but this will be automatically added with command: ng g c YourComponent 🙂

  12. Hello, how are you? Sorry for my bad English. I write to you from Argentina. Everything worked perfect, however, I load the pages without styles, I can not attach a screen print, but it is only forms and titles without styles. With the development tool I don’t see any error in loading any resources. Could you help me?
    From already thank you very much,

    Best regards,

Comments are closed to reduce spam. If you have any question, please send me an email.