Angular applications can communicate with any web server supporting HTTP, regardless of what server-side platform is used. Angular 4.3 introduces an Angular httpClient module to request a call to the HTTP request and we’ll learn how to use Angular HTTP methods.
We have few objectives in this tutorial, first we’ll look at overview of Angular HTTPClient module. Second, we will learn different HTTP methods, third how to use HTTP with promise and observable.
We can define HTTP requests as Services, services are user-defined classes used to access the database and from the server site and other activities. The front-end can’t make a request directly to the database because of a security issue. Angular recommends only having template-specific codes in components. A component’s responsibility is to enrich the UI/UX in the Angular application and delegate business logic to services. Components are consumers of services.
What is Angular Httpclient Module
Angular supports asynchronous HTTP communications via the HttpClient service from the @angular/common/http package. For the HTTP request sent by the browser on behalf of an application, we need to import HttpClient Module to use the HTTP request.
The new HttpClient service is included in HttpClientModule and can be used to initiate HTTP requests and process responses within your application. Angular httpClient Module is an upgraded version of HTTP from the @angular/http module with the following improvements:
- JSON is an assumed default and no longer needs to be explicitly parsed.
- Interceptors allow middleware logic to be inserted into the pipeline.
- Immutable request/response objects progress events for both request upload and response download and more.
Step 1: Configure and setup Angular Http project<
Let first create our Angular Http project by using the following command and we don’t have any dependency on another library for HTTP requests. We need to create a service to make HTTP requests to a server and lastly create a few components to demonstrate HTTP requests.
ng new httpClientApp --routing
cd httpClientApp
ng generate service services/todos
<-- Add few component to demonstrate Angular Http post & other methods -->
ng generate component components/home
ng generate component components/todos
ng generate component components/home
ng generate component components/edit-todo
ng generate component components/new-todo
We have a few components, in the todos component will use the Angular HTTP GET method, in this component we have the HTTP DELETE method. In the new todo and edit todo components, we are demonstrating HTTP POST and PUt requests. The HTPP we can perform the following request.
Method | Description |
GET | Doesn’t contain a request body. GET or retrieve a list of entities from the server. |
POST | Create a new entity for the server. |
PUT | Override the whole entity. |
PATCH | Replace a subset of an entity. |
DELETE | Delete entity from the server. |
In our app.routing.module.ts let’s add routing for our components.
...
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'todos', component: TodosComponent },
{ path: 'todo/:id', component: EditTodoComponent },
];
...
export class AppRoutingModule { }
And in our app.component.html template, we have added a navigation bar using the bootstrap framework. If you want to know the best way to install and used bootstrap in Angular. Then check our previous articles on how to install ng-bootstrap in Angular.
Let’s edit the app.component.html template to add a navbar.
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" routerLink="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="todos">Todos</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<router-outlet></router-outlet>
</div>
Create a todo interface
In our HTTP request, we are performing a strict type request on different HTTP methods. For that, we need to create an interface of todo, let’s create folder models and add the todo.model.ts file with the following type.
export interface ITodo {
id?: string;
task: string;
status: string;
}
Import Angular HttpClient module
To make an HTTP call request we have to import the HttpClient module. We can import this module into the application in either the root module or one of the feature modules before HTTP. We will import this module and FormsModule in our root module i.e the app.module.ts file.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TodosComponent } from './components/todos/todos.component';
import { HttpClientModule } from '@angular/common/http';
import { NewTodoComponent } from './components/new-todo/new-todo.component';
import { EditTodoComponent } from './components/edit-todo/edit-todo.component';
import { FormsModule } from '@angular/forms';
import { HomeComponent } from './components/home/home.component';
@NgModule({
declarations: [
AppComponent,
TodosComponent,
NewTodoComponent,
EditTodoComponent,
HomeComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
NgbModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Setting up the server for HTTP request
Setting up the server and adding data to the database will make our tutorial very long. We’ll use JSON Server to allow us to make fake HTTP requests without any coding and let install it globally.
npm install json-server
All the HTTP requests to JSON Server are the same as Node-based express-js.
Create Dummy data for HTTP Request
We have almost completed all configurations needed for setting up HTTP requests and we have left with our data. Let create dummy data in src/data/todo.json and add the following data.
{
"todos": [
{
"id": "1",
"task": "Meditation at 7 am",
"status": "pending"
},
{
"id": "2",
"task": "Task 4 creation of blog post",
"status": "finished"
},
{
"task": "Task 3 ",
"status": "finished",
"id": "3"
},
{
"id": "4",
"task": "Task 4 todo",
"status": "pending"
}
]
}
To request or access dummy data, we need to run our fake server using the following command in our project folder on the terminal.
json-server src/assets/data/todos.json
Note: We need to run the above command to make an HTTP request. Now you can access JSON data in our URL as follows.
Step 2: Create a service to request HTTP request
We have to create a service class to request HTTP requests to a remote server. In the HTTP Service, we’ll encapsulate some logic that helps manage the list of todos into service, edit, delete and create new todos using the HttpClient service from Angular.
We have already created todo.service.ts in the app/services folder. We have to import HttpClient from HttpClientModule and inject the HttpClient dependency in the constructor.
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs'
import { ITodo } from '../model/todo.model';
import { catchError } from 'rxjs/operators';
const URL_PREFIX = "http://localhost:3000";
@Injectable({
providedIn: 'root'
})
export class TodosService {
constructor(private http: HttpClient) { }
getTodos(): Observable<Array<ITodo>> {
return this.http.get<Array<ITodo>>(`${URL_PREFIX}/todos`,);
}
getTodo(id: string): Observable<ITodo> {
return this.http.get<ITodo>(`${URL_PREFIX}/todos/${id}`);
}
addTodo(todo: ITodo) {
return this.http.post(`${URL_PREFIX}/todos`, todo)
.pipe(
catchError((error: HttpErrorResponse) => {
console.log(error.message);
return throwError("Error while creating a todo" + error.message);
}));
}
updateTodo(todo: ITodo, id: string): Observable<ITodo> {
return this.http.put<ITodo>(`${URL_PREFIX}/todos/${id}`, todo)
.pipe(
catchError((error: HttpErrorResponse) => {
// this.errorHandler.log("Error while updating a todo", error);
console.log(error.message);
return throwError("Error while updating a todo " + error.message);
}));
}
patchTodo(todo: ITodo, id: string): Observable<ITodo> {
return this.http.patch<ITodo>(`${URL_PREFIX}/todos/${id}`, todo)
.pipe(
catchError((error: HttpErrorResponse) => {
console.log(error.message);
return throwError("Error while updating a todo " + error.message);
}));
}
deleteTodo(id: string) {
return this.http.delete(`${URL_PREFIX}/todos/${id}`).pipe(
catchError((error: HttpErrorResponse) => {
console.log(error.message);
return throwError("Error while deleting a todo " + error.message);
}));
}
}
In our service, we have performed all HTTP Requests, we have const URL_PREFIX containing the URL of our server.
Angular HTTP requests are asynchronous and we can handle asynchronous by three different approaches.
- Callbacks: Is not recommended and will not use it.
- Promises
- Observables.
Angular HTTP GET method
In our Angular service, we have used the getTodos() observable to retrieve all todos from our server localhost:3000 or remote server. In our todo service, we have used the HttpClient object get method to get call HTTP requests.
Angular uses typescript which allows us to return explicitly type, in our HTTP GET we return observable of type ITodo array only. Uses of explicit type will reduce error.
Now in the todos component, we can inject our todos service and call getTodos() methods. This method in turn returns observable which we can subscribe in our consumer components. Let edit the todos.component.ts file.
import { Component, OnInit } from '@angular/core';
import { ITodo } from 'src/app/model/todo.model';
import { TodosService } from 'src/app/services/todos.service';
@Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.scss']
})
export class TodosComponent implements OnInit {
todos: ITodo[] = [];
constructor(private todoService: TodosService) { }
ngOnInit(): void {
this.todoService.getTodos()
.subscribe((data: ITodo[]) => this.todos = data);
}
deleteTodo(todo: ITodo) {
let id = todo.id ? todo.id : '';
this.todoService.deleteTodo(id).subscribe(() => {
alert('Delete todo : ');
})
}
}
In our todo component, we have injected Angular HTTP service and call both todos and delete methods. Subscribing to HTTP GET will return either list of todos or errors while calling a remote HTTP request.
Let edit the todos.component.html to loop through the todos array, and add a button to delete by sending the id of todo in the HTTP Delete method. We also have an edit button to route to the todo-edit component, where we can edit our todo.
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Task</th>
<th scope="col">Status</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let todo of todos; index as i">
<th scope="row">{{ i + 1 }}</th>
<td>{{ todo.task }}</td>
<td>
<span *ngIf="todo.status == 'finished'">
Completed
</span>
<span *ngIf="todo.status == 'pending'">
Pending
</span>
</td>
<td>
<button [routerLink]="['/todo/' + todo.id]">Edit</button>
</td>
<td>
<i (click)="deleteTodo(todo)" class=" bi bi-trash icon-red"></i>
</td>
</tr>
</tbody>
</table>
Angular HTTP GET method using observable and promise
In our previous example in Angular HTTP service, we have called HTTP GET on todos with only return type Observable. We can use Promise and Observable on HTTP GET, let’s edit our todos.service.ts file to add the following code.
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, Observer, throwError } from 'rxjs'
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/operators';
import { ITodo } from '../models/todo.model';
const URL_PREFIX = "http://localhost:3000";
.....
//HTTP GET with observable
getTodosObservable(): Observable<Array<ITodo>> {
return new Observable((observer: Observer<Array<ITodo>>) => {
let results: Array<ITodo> = [];
this.http.get<Array<ITodo>>(`${URL_PREFIX}/todos`)
.subscribe((data: Array<ITodo>) => {
data.map(i => {
results.push(i)
});
observer.next(results);
observer.complete();
},
// () => ({/** Error handling code goes here */ }),
// () => ({/** Observable complete */ })
);
});
}
// HTTP GET with promise
getPromiseTodos(): Promise<Array<ITodo>> {
return new Promise<Array<ICountry>>((resolve) => {
let results: Array<ITodo> = [];
this.httpClient.get<Array<ITodo>>(`${URL_PREFIX}/todos`)
.subscribe((data: Array<ITodo>) => {
data.map(i => {
results.push(i)
});
resolve(results);
},
() => ({/** Error handling code goes here */ }),
() => ({/** Observable complete */ })
);
});
}
}
Let us use observable on our consumer components, here we can use the async pipe on observable and let demonstrate it in our todos component.
import { Component, OnInit } from '@angular/core';
import { ITodo } from 'src/app/model/todo.model';
import { TodosService } from 'src/app/services/todos.service';
@Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.scss']
})
export class TodosComponent implements OnInit {
todos$: Observable<Array<ITodo>> | undefined;
constructor(private todoService: TodosService) {
this.todos$ = this.todoService.getTodosObservable();
// For promise http GET
// this.todoService.getPromiseTodos()
//.then((data: ICountry[]) => this.countries = data);
}
Now in our todos template, we can use the async pipe on the todos$ observable as follows.
...
<tbody>
<tr *ngFor="let todo of todos$ | async; index as i">
<th scope="row">{{ i + 1 }}</th>
<td>{{ todo.task }}</td>
<td>
<span *ngIf="todo.status == 'finished'">
Completed
</span>
<span *ngIf="todo.status == 'pending'">
Pending
</span>
</td>
<td>
<button [routerLink]="['/todo/' + todo.id]">Edit</button>
</td>
<td>
<i (click)="deleteTodo(todo)" class=" bi bi-trash icon-red"></i>
</td>
</tr>
</tbody>
...
Angular HTTP POST example
To add a new entity to our database server, we can use the HTTP POST method. In our new todo component, we are calling addTodo to add a new todo to our todos.json file.
We had already added HTTP POST to add new todo in our todos.service.ts file using the Angular HTTPClient object. On our consumer component, new-todo let’s add code to add new todo and form to accept data. In a real application, we don’t need to add an id, this is handle by the server but in our case, we are using a fake JSON server.
import { Component } from '@angular/core';
import { ITodo } from 'src/app/model/todo.model';
import { TodosService } from 'src/app/services/todos.service';
@Component({
selector: 'app-new-todo',
templateUrl: './new-todo.component.html',
styleUrls: ['./new-todo.component.scss']
})
export class NewTodoComponent {
constructor(private todoService: TodosService) { }
addTodo(todo: ITodo) {
this.todoService.addTodo(todo).subscribe((data: any) => {
alert('Add new task successful' + data);
})
}
}
In the new.todo.component.html template add Angular form to accept data from users.
<div class=form-container>
<h6>Add new todo</h6>
<form #taskForm="ngForm" (ngSubmit)="addTodo(taskForm.value)">
<div class="form-group">
<label>ID</label>
<input type="text" name="id" class="form-control" ngModel>
</div>
<div class="form-group">
<label>Task name</label>
<input type="text" name="task" class="form-control" ngModel>
</div>
<div class="form-group radio">
<label>Status</label>
<input type="radio" name="status" class="form-control" value="finished"
ngModel> Completed
<input type="radio" name="status" class="form-control" value="pending"
ngModel> Pending
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</form>
</div>
When we enter the new todo and click on submit button will invoke addTodo which intern calls HTTP POST to add new todo and return add todo.
Angular HTTP PUT example
At last, we can demonstrate HTTP PUT and PATCH, both are used to edit data in the database server. The PUT method will override the whole entity and PATCH will edit the subset of the entity.
When the user clicks on the edit button of the corresponding todo in the todo list, the Angular application will navigate to a todo-edit component by using http://localhost:4200/todo/2.
Once we are in the edit-todo component we can retrieve the corresponding todo by using its id from the URL and ActivatedRoute object. Clicking on the update button will invoke Angular HTTP PUT to override the existing todo with the new value.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ITodo } from 'src/app/model/todo.model';
import { TodosService } from 'src/app/services/todos.service';
@Component({
selector: 'app-edit-todo',
templateUrl: './edit-todo.component.html',
styleUrls: ['./edit-todo.component.scss']
})
export class EditTodoComponent implements OnInit {
todo = {
id: '',
task: '',
status: ''
};
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private todoService: TodosService
) { }
ngOnInit(): void {
let id = this.activatedRoute.snapshot.params['id'];
if (id) {
this.todoService.getTodo(id).subscribe((data: any) => {
this.todo = {
id: data.id,
task: data.task,
status: data.status
}
})
}
}
updateTodo(todo: ITodo) {
this.todoService.updateTodo(todo, this.todo.id)
.subscribe((data) => {
alert('Successful update');
this.router.navigateByUrl('todos');
})
}
}
In our edit-todo template add the todo edit form, to allow the user new data for todo.
<div class=form-container>
<h6>Edit Todo</h6>
<form #taskForm="ngForm" (ngSubmit)="updateTodo(taskForm.value)">
<div class="form-group">
<label>Task name</label>
<input type="text" name="task" class="form-control" [(ngModel)]="todo.task">
</div>
<div class="form-group radio">
<label>Status</label>
<input type="radio" name="status" class="form-control" value="finished" [(ngModel)]="todo.status">Completed
<input type="radio" name="status" class="form-control" value="pending" [(ngModel)]="todo.status"> Pending
</div>
<div class="form-group float-right">
<button class="btn btn-primary" type="submit">Update</button>
</div>
</form>
</div>
Conclusion
Angular httpClient methods allow us to communicate to a remote server through REST API. We can also use a third party like the ngx-restangular to make HTTP requests to the server. It is best practice to keep HTTP requests code at service, and components are consumers of service. I have uploaded the above code to the Github repository, if it is helpful then you check the code there.
Related Articles
- How to implement an Angular bootstrap table with pagination and filter
- How to add Angular signature pad in Angular 12 | 13 ?
- How to install Angular material?
- How to implement CKEditor in Angular ?