angular-10-crud-app-feature-image

Angular 10 CRUD Application example with Web API

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

Other version: Angular 8 CRUD Application example with Web API

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

Authentication: Angular 10 JWT Authentication example with Web Api

More Practice:
Angular 10 File upload example with progress bar
Angular 10 Multiple Files upload example

Overview of Angular 10 CRUD Application

We will build an Angular 10 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-10-crud-app-create

– Retrieve all Tutorials:

angular-10-crud-app-retrieve-all

– Click on Edit button to update a Tutorial:

angular-10-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-10-crud-app-search

Web API

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

MethodsUrlsActions
POST/api/tutorialscreate new Tutorial
GET/api/tutorialsretrieve all Tutorials
GET/api/tutorials/:idretrieve a Tutorial by :id
PUT/api/tutorials/:idupdate a Tutorial by :id
DELETE/api/tutorials/:iddelete a Tutorial by :id
DELETE/api/tutorialsdelete 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 & MongoDb
Spring Boot & MySQL/PostgreSQL
Spring Boot & MongoDB
Django & MySQL
Django & PostgreSQL
Django & MongoDB

All of them can work well with this Angular App.

Angular 10 CRUD App Component Diagram

angular-10-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 10 Project

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

ng new Angular10Crud
? 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-10-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 10 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';
import { Observable } from 'rxjs';

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

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

  constructor(private http: HttpClient) { }

  getAll(): Observable<any> {
    return this.http.get(baseUrl);
  }

  get(id): Observable<any> {
    return this.http.get(`${baseUrl}/${id}`);
  }

  create(data): Observable<any> {
    return this.http.post(baseUrl, data);
  }

  update(id, data): Observable<any> {
    return this.http.put(`${baseUrl}/${id}`, data);
  }

  delete(id): Observable<any> {
    return this.http.delete(`${baseUrl}/${id}`);
  }

  deleteAll(): Observable<any> {
    return this.http.delete(baseUrl);
  }

  findByTitle(title): Observable<any> {
    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(): void {
  }

  saveTutorial(): void {
    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(): void {
    this.submitted = false;
    this.tutorial = {
      title: '',
      description: '',
      published: false
    };
  }

}

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

<div style="width: 400px; margin: auto;">
  <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>
</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(): void {
    this.retrieveTutorials();
  }

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

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

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

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

  searchTitle(): void {
    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" routerLink="/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 10 Pagination example | ngx-pagination

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(): void {
    this.message = '';
    this.getTutorial(this.route.snapshot.paramMap.get('id'));
  }

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

  updatePublished(status): void {
    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(): void {
    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(): void {
    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 style="width: 400px; margin: auto;">
  <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>
</div>

Run the Angular 10 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 & MongoDb
Spring Boot & MySQL/PostgreSQL
Spring Boot & MongoDB
Django & MySQL
Django & PostgreSQL
Django & MongoDB

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

This is our Angular 10 CRUD application demo and brief instruction:

Conclusion

Today we’ve built an Angular 10 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 also find how to implement Authentication with the post:
Angular 10 JWT Authentication example with Web Api

Or you can add Pagination Component:
Angular 10 Pagination example | ngx-pagination

angular-10-pagination-example-ngx-default-paging

Or implement File Upload Component:
Angular 10 File upload example with progress bar

Happy learning, see you again!

Further Reading

Source Code

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

Serverless with Firebase:
Angular 10 Firebase CRUD Realtime Database
Angular 10 Firestore CRUD with AngularFireStore

26 thoughts to “Angular 10 CRUD Application example with Web API”

  1. Hi, Dear bezkoder .i don’t know how to say thank you for your amazing and helpful tutorial. I search whole tutorials on the internet to help me in my project, but none of those works for me, at last, I found your website and your tutorial fixes my issue. Thanks

  2. This tutorial and the Django REST Framework tutorial are AWESOME! Thank you so much for publishing this straightforward tutorial. I’ve spent so many hours trying to find helpful info, yet you simplified it in such a way that I can actually understand it all. Thank you again!

  3. Hello, I have gotten everything working but mine does not look as nice as yours. Do I need to update my css files to make it look like yours? If so, what do your css files look like?

  4. Hey, thank you so much for your tutorials! When I click ‘update’ on a tutorial I get the error:
    cannot get http://localhost:8080/tutorials/tutorials
    this part: this.getTutorial(this.route.snapshot.paramMap.get(‘id’));
    does not seem to get the tutorial id but instead gets the name tutorial.

    please do you know why?

    1. Hi, the url for specific tutorial with id=42 must be http://localhost:8080/tutorials/42.
      this.getTutorial(this.route.snapshot.paramMap.get(‘id’)); will get 42 and send an HTTP GET tutorials/42 to the server.

  5. hello,
    I run the code on localhost but after clicking on a button or adding a tutorial it is not reflecting in database.
    what must have gone wrong? Please reply

  6. Hello bezkoder,
    this tutorial is very valuable in that sense that you have described each and every thing very smoothly in it. But i don’t know why i am not getting response on console and error like,
    (HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: “Unknown Error”, url: “http://localhost:8080/api/tutorials”, ok: false, …})

    I have only tried to implement addTutorial and not getting response.
    kindly help me out.

  7. Please, I am using angular, mssql and aws lambda. Is there a way to perform the CRUD operation this way? Thank you

Leave a Reply

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