Rest API with Angular

Mercy Jemosop
8 min readSep 9, 2021

Consuming the JSON REST API with Angular HttpClient

Introduction

Angular provides a client HTTP API for Angular applications, the HttpClient service class in @angular/common/http.

Before we start I’m assuming you are familiar with routing basics. I have attached a link for a quick overview.

Setup server communication

To use HttpClient, we first need to import HttpClientModule in the root app module .app/app.module.ts

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

Inside the @NgModules imports add the HttpClientModule

// import HttpClientModule after BrowserModule.
HttpClientModule,

Consuming the JSON REST API with Angular HttpClient

To do this we need to create an Angular service for encapsulating the code that deals with consuming data from Rest Api server.

A service is a singleton that can be injected by other services and components using the Angular dependency injection.

Components shouldn’t fetch or save data directly and they certainly shouldn’t knowingly present fake data. They should focus on presenting data and delegate data access to a service.

Services are a great way to share information among classes that don’t know each other.

create a service

ng generate service user 
or
ng g s user

Create a get request method to fetch data from API

public getUsers(): Observable<any> { 
return this.httpClient.get(this.baseUrl);
}

The @Injectable() decorator marks the class as one that participates in the dependency injection system. It accepts a metadata object for the service.

Class

Create a class to specify the required fields. Creates a new, generic class definition in the given or default project.

ng g class <name> [options]
ng g class User

<name> is the name of the new class.

add data to the class. Add the name of one of the response from rest API

export class User {
id!: number;
name!: String;
occupation!: String;
email!: String;
bio!: String;
}

Call the API function in the service class from component.ts class.

OnInit

A lifecycle hook that is called after Angular has initialized all data-bound properties of a directive. Define an ngOnInit() method to handle any additional initialization tasks. A callback method that is invoked immediately after the default change detector has checked the directive’s data-bound properties for the first time, and before any of the view or content children have been checked. It is invoked only once when the directive is instantiated.

Display the users in a table by looping through the elements in users. User details route is handled below in case you have an error, finish the blog before running the code on the browser or you can comment out the section for now.

Hope all goes well.

Get a user by id:

Create a route by Updating the app-routing.module.ts with the code below

import { UserDetailsComponent } from './user-details/user-details.component';const routes: Routes = [
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailsComponent }
];

Create a method to get user in the service class. Update the service class above.

public getUser(id: number): Observable<Object> {
return this.httpClient.get(`${this.baseUrl}/${id}`);
}

Add a details button in list table.(users-list.component.html)

<th>Action</th>//and its table data<td> 
<button (click)="userDetails(user.id)" class='btn btn-info' data- toggle="modal"data-target="#myModal">Details</button>
</td>

Update the user-list.component.ts with the route functionality. When a user taps a button it navigates to the new page with the id of the user.

userDetails(id: number) {
this.router.navigate(['users', id]);
console.log(id);
}

When you click on the details button in users list, it should navigate to a new page with the user id on the browser.

Get data to the new page

Create a form to display data in the new component.html

<h2>Users Details</h2><hr /><div *ngIf="user"> <div> 
<label><b>First Name: </b></label> {{user.name}}
</div>
<div>
<label><b>Email </b></label> {{user.email}}
</div>
<div>
<label><b>Occupation: </b></label> {{user.email}}
</div>
<div>
<label><b>Bio: </b></label> {{user.bio}}
</div>
</div><br><br><button (click)=" usersList()" class="btn btn-primary">Back to User List</button><br>

Call the user details API from the component class

ActivatedRoute

Provides access to information about a route associated with a component that is loaded in an outlet. Use to traverse the RouterState tree and extract information from nodes.

Improve the UI with bootstrap and material design

install material design

npm install --save @angular/material

Update your app.module.ts with the packages you installed

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { UserListComponent } from './user-list/user-list.component';
import { AppRoutingModule } from './app-routing.module';
import { UserDetailsComponent } from './user-details/user-details.component';
import { UpdateUserComponent } from './update-user/update-user.component';
import { FormsModule } from '@angular/forms';
import { AddUserComponent } from './add-user/add-user.component';
import { MatButtonModule } from '@angular/material/button';
import { MatTableModule } from '@angular/material/table';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatPaginatorModule } from '@angular/material/paginator';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
declarations: [
AppComponent,
UserListComponent,
UserDetailsComponent,
UpdateUserComponent,
AddUserComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
MatButtonModule,
MatTableModule,
MatToolbarModule,
MatPaginatorModule,
NgbModule,
MatProgressSpinnerModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Install bootstrap

npm i bootstrap

check if bootstrap is added to your project. open angular.json. You can check inside styles

There are other methods of installing these packages if you encounter an error.

Create a table using material design

update user-list.component.html

//title
<div class=" border border-class">
<h2 class="h2-class">User List</h2>
</div>
///Add button
<button mat-raised-button class="btn btn-info " style="float: right; " (click)="addUser()">Add</button>
<!-- style="float: right; margin:10px; -->

///search button
<div class="col-md-3 " style="margin:10px "> <form>
<input type="text" [(ngModel)]="email" class="form-control " (input)="search()" name="email" placeholder="search email">
</form>
</div>

/// users list table
<div>
<mat-table class="users-table mat-elevation-z8" [dataSource]="users" matSort>
<!-- Name Column -->
<ng-container matColumnDef="name"> <mat-header-cell *matHeaderCellDef>Name</mat-header-cell> <mat-cell *matCellDef="let user ">{{user.name}}</mat-cell> </ng-container>
<!-- email Column -->
<ng-container matColumnDef="email"> <mat-header-cell *matHeaderCellDef>Email</mat-header-cell> <mat-cell *matCellDef="let user ">{{user.email}}</mat-cell> </ng-container>
<!-- bio Occupation -->
<ng-container matColumnDef="occupation"> <mat-header-cell *matHeaderCellDef>Occupation</mat-header-cell> <mat-cell *matCellDef="let user ">{{user.occupation}}</mat-cell> </ng-container>
<ng-container matColumnDef="action"> <mat-header-cell *matHeaderCellDef> Action </mat-header-cell> <mat-cell *matCellDef="let user"> <button mat-raised-button class="btn btn-info" (click)="userDetails(user.id)">Details</button> </mat-cell>
</ng-container>
<ng-container matColumnDef="actionu"> <mat-header-cell *matHeaderCellDef> Action </mat-header-cell> <mat-cell *matCellDef="let user"> <button mat-raised-button class="btn btn-info " (click)="editUser(user.id)">Update</button> </mat-cell>
</ng-container>
<ng-container matColumnDef="actiond"> <mat-header-cell *matHeaderCellDef> Action </mat-header-cell> <mat-cell *matCellDef="let user"> <button mat-raised-button class="btn btn-danger" (click)="deleteUser(user.id)">Delete</button> </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>

<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>

<div *ngIf="isLoading" style="display: flex; justify-content: center; align-items: center; background: white;"> <h3>Loading.....</h3>
</div>
</div>

here is a link to a more readable code

dataSource is an input data array passed to a table to provide data array to a table.

<mat-table class="users-table mat-elevation-z8" [dataSource]="users" matSort>

mat-table provides a Material Design styled data-table that can be used to display rows of data.

To display the row title we need to define

In our component.ts , create a variable in your component that contains the list of the columns you want to render.

displayedColumns = ['name', 'email', 'occupation', 'action', 'actionu', 'actiond'];

Then add mat-header-row and mat-row to the content of your mat-table and provide your column list as inputs.

<mat-header-row *matHeaderRowDef="columnsToDisplay"></mat-header-row>
<mat-row *matRowDef="let myRowData; columns: columnsToDisplay"></mat-row>

To change the size of the individual columns. here is a css file for that.Add this to your style.css file. The css below reduces the size of the action buttons.

.mat-column-action {
flex: 0 0 10%;
}
.mat-column-actionu {
flex: 0 0 10%;
}
.mat-column-actiond {
flex: 0 0 10%;
}
.mat-header-cell {
background-color:navy ;
color: white;
}
h2 {
text-align: center;
}
.border-class{
background-color: rgb(208, 216, 224);
}
.h2-class{
font-style: italic;
font-size: 40px;
font-weight: 900;
color: navy;
}
.tp-section {
display: flex;
align-content: center;
align-items: center;
height: 60px;
}
.tp-margin {
margin: 0 10px;
}

update your user-list.component.ts

I have added a method to delete and router’s to navigate to add,update and details page. You need to create components for the add, update and details page as we had done above

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { User } from '../user';
import { UserService } from '../user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users!: User[];
id!: number;
user: User = new User();
email!: String; //search
isLoading = true;
displayedColumns = ['name', 'email', 'occupation', 'action', 'actionu', 'actiond'];constructor(private userService: UserService, private router: Router,) { }ngOnInit() {
this.userService.getUsers().subscribe(data => {
console.log(data);
this.users = data;
this.isLoading = false;
})
}
userDetails(id: number) {
this.router.navigate(['user', id]);
console.log(id);
}
deleteUser(id: number) {
this.userService.deleteUser(id).
subscribe(data => {
console.log(data);
this.ngOnInit();
},
error => console.log(error));
}
editUser(id: number) {
this.router.navigate(['update', id]);
console.log(id);
}
addUser() {
this.router.navigate(['add']);
}
search() {this.isLoading = true;this.users = this.users.filter(res => {
if (!this.users || !this.email) {this.userService.getUsers().subscribe(data => {this.users = data;console.log(data);})
}
else {(error: any) => console.log(error);}
return res.email.toLocaleLowerCase().match(this.email.toLocaleLowerCase());
})}}

github link

Update the routes

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserListComponent } from './user-list/user-list.component';
import { UserDetailsComponent } from './user-details/user-details.component';
import { UpdateUserComponent } from './update-user/update-user.component';
import { AddUserComponent } from './add-user/add-user.component';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'users' },
{ path: 'users', component: UserListComponent },
{ path: 'user/:id', component: UserDetailsComponent },
{ path: 'update/:id', component: UpdateUserComponent },
{ path: 'add', component: AddUserComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

N.B if you did not remember to edit the app.component.html.

<router-outlet></router-outlet>

Finally update the user.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({providedIn: 'root'})export class UserService {public baseUrl = "http://localhost:8081";constructor(private httpClient: HttpClient) {}public getUsers(): Observable<any> {
return this.httpClient.get(`${this.baseUrl}/users`);
}
public getUser(id: number): Observable<any> {
return this.httpClient.get(`${this.baseUrl}/user/${id}`);
}
public deleteUser(id: number): Observable<any> {
return this.httpClient.delete(`${this.baseUrl}/deleteUser/${id}`, { responseType: 'text' });
}
public updateUser(id: number, value: any): Observable<any> {
return this.httpClient.put(`${this.baseUrl}/userUpdate/${id}`, value);
}
public addUser(user: object): Observable<object> {
return this.httpClient.post(`${this.baseUrl}/registration`, user);
}
}

Here is a link to the complete github code.

The above code will display users list and a specific user details. Improve the UI for better appearance.

Happy coding!!!!!!!!!!!!

--

--

Mercy Jemosop

Software Developer. I am open to job referrals. connect with me on twitter @kipyegon_mercy