In this tutorial, I will show you how to build an Angular 14 project with CRUD Application example to consume Rest APIs, display, modify & search data using HttpClient, Forms and Router.
Other versions:
– Angular 8 CRUD example with Web API
– 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 15 CRUD example with Web API
Fullstack CRUD Application:
– Angular + Node Express + MySQL example
– Angular + Node Express + PostgreSQL example
– Angular + Node Express + MongoDB example
– Angular + Spring Boot + H2 example
– Angular + Spring Boot + MySQL example
– Angular + Spring Boot + PostgreSQL example
– Angular + Spring Boot + MongoDB example
– Angular + Django example
– Angular + Django + MySQL example
– Angular + Django + PostgreSQL example
– Angular + Django + MongoDB example
Authentication: Angular JWT Authentication example with Web Api
More Practice:
– Angular Form Validation example (Reactive Forms)
– Angular File upload example with Progress bar
– Angular Multiple Files upload example with Progress Bar
Contents
- Overview
- Rest API
- Technology
- Angular App Component Diagram
- Setup Angular 14 Project
- Project Structure
- Set up App Module
- Define Routes for Angular AppRoutingModule
- Import Bootstrap
- Add Navbar and Router View
- Define Model Class
- Create Data Service
- Create Angular Components
- Run the Angular 14 CRUD example
- Source Code
- Conclusion
- Further Reading
Overview of Angular 14 CRUD example
We will build an Angular 14 example project – 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 14 CRUD Application.
– Create a Tutorial:
– Retrieve Tutorials:
– Click on Edit button to update an object:
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
If you want to add form validation, please visit:
Angular Form Validation example (Reactive Forms)
– Search Tutorials by title:
Rest 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
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
All of them can work well with this Angular 14 project.
Technology
- Angular 14
- RxJS 7
- Bootstrap 4
Angular 14 CRUD App Component 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.
– TutorialDetails
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 14 Project
Let’s open cmd and use Angular CLI to create a new Angular Project as following command:
ng new angular-14-crud-example
? 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 class models/tutorial --type=model
ng g c components/add-tutorial
ng g c components/tutorial-details
ng g c components/tutorials-list
ng g s services/tutorial
Now you can see that our project directory structure looks like this.
Let me explain it briefly.
– tutorial.model.ts
exports the main class model: Tutorial
.
– 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.
– style.css
contains import for Bootstrap file.
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 14 Project
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 { RouterModule, Routes } 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 { }
Import Bootstrap into Angular 14 Project
Run the command: npm install [email protected]
.
Next, open src/style.css and add following code:
@import "~bootstrap/dist/css/bootstrap.css";
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>
Define Model Class
Our main model class Tutorial
will be exported in tutorial.model.ts with 4 fields:
id
title
description
published
models/tutorial.model.ts
export class Tutorial {
id?: any;
title?: string;
description?: string;
published?: boolean;
}
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';
import { Tutorial } from '../models/tutorial.model';
const baseUrl = 'http://localhost:8080/api/tutorials';
@Injectable({
providedIn: 'root'
})
export class TutorialService {
constructor(private http: HttpClient) { }
getAll(): Observable<Tutorial[]> {
return this.http.get<Tutorial[]>(baseUrl);
}
get(id: any): Observable<Tutorial> {
return this.http.get<Tutorial>(`${baseUrl}/${id}`);
}
create(data: any): Observable<any> {
return this.http.post(baseUrl, data);
}
update(id: any, data: any): Observable<any> {
return this.http.put(`${baseUrl}/${id}`, data);
}
delete(id: any): Observable<any> {
return this.http.delete(`${baseUrl}/${id}`);
}
deleteAll(): Observable<any> {
return this.http.delete(baseUrl);
}
findByTitle(title: any): Observable<Tutorial[]> {
return this.http.get<Tutorial[]>(`${baseUrl}?title=${title}`);
}
}
Create Angular 14 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 { Tutorial } from 'src/app/models/tutorial.model';
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: 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({
next: (res) => {
console.log(res);
this.submitted = true;
},
error: (e) => console.error(e)
});
}
newTutorial(): void {
this.submitted = false;
this.tutorial = {
title: '',
description: '',
published: false
};
}
}
components/add-tutorial/add-tutorial.component.html
<div>
<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>Tutorial was submitted successfully!</h4>
<button class="btn btn-success" (click)="newTutorial()">Add</button>
</div>
</div>
</div>
components/add-tutorial/add-tutorial.component.css
.submit-form {
max-width: 400px;
margin: auto;
}
List of items Component
This component calls 3 TutorialService
methods:
getAll()
deleteAll()
findByTitle()
It also contains a child component tutorial-details
which we will create later.
components/tutorials-list/tutorials-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Tutorial } from 'src/app/models/tutorial.model';
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?: Tutorial[];
currentTutorial: Tutorial = {};
currentIndex = -1;
title = '';
constructor(private tutorialService: TutorialService) { }
ngOnInit(): void {
this.retrieveTutorials();
}
retrieveTutorials(): void {
this.tutorialService.getAll()
.subscribe({
next: (data) => {
this.tutorials = data;
console.log(data);
},
error: (e) => console.error(e)
});
}
refreshList(): void {
this.retrieveTutorials();
this.currentTutorial = {};
this.currentIndex = -1;
}
setActiveTutorial(tutorial: Tutorial, index: number): void {
this.currentTutorial = tutorial;
this.currentIndex = index;
}
removeAllTutorials(): void {
this.tutorialService.deleteAll()
.subscribe({
next: (res) => {
console.log(res);
this.refreshList();
},
error: (e) => console.error(e)
});
}
searchTitle(): void {
this.currentTutorial = {};
this.currentIndex = -1;
this.tutorialService.findByTitle(this.title)
.subscribe({
next: (data) => {
this.tutorials = data;
console.log(data);
},
error: (e) => console.error(e)
});
}
}
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">
<app-tutorial-details
[viewMode]="true"
[currentTutorial]="currentTutorial"
></app-tutorial-details>
</div>
</div>
If you click on Edit button of any Tutorial, You will be directed to Tutorial page with url: /tutorials/:id
.
This is how we embed the child component:
– We use the child’s selector, here <app-tutorial-details>
, as a directive within the parent template.
– We use property binding to bind the viewMode
and currentTutorial
property in the child to the currentTutorial
property of the parent.
So the tutorial-details
component will look like this:
...
@Component({
selector: 'app-tutorial-details',
...
})
export class TutorialDetailsComponent implements OnInit {
@Input() viewMode = false;
@Input() currentTutorial: Tutorial = {
title: '',
description: '',
published: false
};
...
}
With @Input()
decorator in the child component class, Angular passes the value for viewMode
and currentTutorial
to the child so that viewMode
renders as true
for example.
components/tutorials-list/tutorials-list.component.css
.list {
text-align: left;
max-width: 750px;
margin: auto;
}
You can add Pagination to this Component, just follow instruction in the post:
Angular Pagination example with ngx-pagination
Item details Component
This component handles 2 things:
– display the current selected Tutorial (from Tutorials List) if viewMode
is true
– display the form (with action buttons) for Tutorial details if viewMode
is false
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, Input, OnInit } from '@angular/core';
import { TutorialService } from 'src/app/services/tutorial.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Tutorial } from 'src/app/models/tutorial.model';
@Component({
selector: 'app-tutorial-details',
templateUrl: './tutorial-details.component.html',
styleUrls: ['./tutorial-details.component.css']
})
export class TutorialDetailsComponent implements OnInit {
@Input() viewMode = false;
@Input() currentTutorial: Tutorial = {
title: '',
description: '',
published: false
};
message = '';
constructor(
private tutorialService: TutorialService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit(): void {
if (!this.viewMode) {
this.message = '';
this.getTutorial(this.route.snapshot.params["id"]);
}
}
getTutorial(id: string): void {
this.tutorialService.get(id)
.subscribe({
next: (data) => {
this.currentTutorial = data;
console.log(data);
},
error: (e) => console.error(e)
});
}
updatePublished(status: boolean): void {
const data = {
title: this.currentTutorial.title,
description: this.currentTutorial.description,
published: status
};
this.message = '';
this.tutorialService.update(this.currentTutorial.id, data)
.subscribe({
next: (res) => {
console.log(res);
this.currentTutorial.published = status;
this.message = res.message ? res.message : 'The status was updated successfully!';
},
error: (e) => console.error(e)
});
}
updateTutorial(): void {
this.message = '';
this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
.subscribe({
next: (res) => {
console.log(res);
this.message = res.message ? res.message : 'This tutorial was updated successfully!';
},
error: (e) => console.error(e)
});
}
deleteTutorial(): void {
this.tutorialService.delete(this.currentTutorial.id)
.subscribe({
next: (res) => {
console.log(res);
this.router.navigate(['/tutorials']);
},
error: (e) => console.error(e)
});
}
}
components/tutorial-details/tutorial-details.component.html
<div *ngIf="viewMode; else editable">
<div *ngIf="currentTutorial.id">
<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>
<ng-template #editable>
<div *ngIf="currentTutorial.id" 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 mb-2"
(click)="updateTutorial()"
>
Update
</button>
<p>{{ message }}</p>
</div>
<div *ngIf="!currentTutorial.id">
<br />
<p>Cannot access this Tutorial...</p>
</div>
</ng-template>
components/tutorial-details/tutorial-details.component.css
.edit-form {
max-width: 400px;
margin: auto;
}
Run the Angular 14 CRUD example
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
– Django & MySQL
– Django & PostgreSQL
– Django & MongoDB
They configure CORS for port 8081
, so you have to run command: ng serve --port 8081
instead.
Conclusion
Today we’ve built an Angular 14 example project – 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.
For adding Form Validation, please visit:
Angular Form Validation example (Reactive Forms)
You can also find how to implement Authentication with the post:
Angular JWT Authentication example with Web Api
Or you can add Pagination Component:
Angular Pagination example with ngx-pagination
Or implement File Upload Component:
Angular File upload example with Progress bar
Happy learning, see you again!
Further Reading
Serverless with Firebase:
– Angular 14 Firebase CRUD with Realtime Database
– Angular 14 Firestore CRUD example
– Angular 14 File Upload with Firebase Storage
Integration:
– How to Integrate Angular with Node.js Restful Services
– How to Integrate Angular with Spring Boot Rest API
Source Code
You can find the complete source code for this tutorial on Github.