HttpClient with Angular 18

Updated : 14/06/2024 danny


We are going to add the HttpClient mechanism to our project to communicate with an HTTP server.

We will use the Angular javascript framework version 18.0.2


What are we going to do ?


This is step 9 of our Angular guide which will allow us to obtain a PWA type Web Application .

The base Angular project we will use already has the following features

  • Generated with Angular CLI
  • Routing
  • Lazy Loading
  • The Bootstrap CSS framework
  • Server Side Rendering
  • Progressive Web App
  • ​​​​​​​Search Engine Optimization

All sources created are indicated at the end of the tutorial.

The application is at the following address


Installation

Our Angular application represents our Frontend part.
We will use Angular's Httpclient module to access a REST API which will represent our Backend part.

The purpose of this tutorial is not to develop a backend, so we will arbitrarily choose an API available on the web.

JSONPlaceholder is a free REST API ideal for tutorials.
It is accessible with the following link https://jsonplaceholder.typicode.com/

The steps will be as follows

  • Create an items module
  • Change the frontend visual interface
  • Perform routing
  • Adapt the module to use the REST API


Let's start by creating our Items module, this one being specific to our application will be created in the modules/application directory.

# Creation of the items module
ng generate component modules/application/example-items/items --flat --module=app
ng generate module modules/application/example-items/items --flat --routing --module=app

# Creating the items module (Method 2)
ng g c modules/application/example-items/items --flat --module=app
ng g m modules/application/example-items/items --flat --routing --module=app

The modules/ application directory is automatically created by Angular CLI

Let's modify the following files

  • app-routing.module.ts
  • app.module.ts
  • home.component.html
  • home.component.css
  • ​​​​​​​items-routing.module.ts
  • items.module.ts
src/app/app-routing.module.ts
  {
    path: 'httpclient',
    loadChildren: () => import('./modules/application/example-items/items.module')
      .then(mod => mod.ItemsModule)
  },
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, isDevMode } from '@angular/core';

import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
import { ServiceWorkerModule } from '@angular/service-worker';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    NotFoundComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: !isDevMode(),
      // Register the ServiceWorker as soon as the application is stable
      // or after 30 seconds (whichever comes first).
      registrationStrategy: 'registerWhenStable:30000'
    }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
src/app/modules/application/example-items/items-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { ItemsComponent } from './items.component';

const routes: Routes = [
  { path: '', component: ItemsComponent },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ItemsRoutingModule { }
src/app/modules/application/example-items/items.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ItemsComponent } from './items.component';
import { ItemsRoutingModule } from './items-routing.module';

@NgModule({
  imports: [
    CommonModule,
    ItemsRoutingModule
  ],
  exports: [
    ItemsComponent
  ],
  declarations: [
    ItemsComponent
  ],
  providers: [
  ],
})
export class ItemsModule { }

At this stage we can test the debugging script to control the architecture of our project.
npm run start

Then type the url
http://localhost:4200/httpclient


Integration

All that remains is to integrate httpclient management into our Items section.

To respect Angular best practices, all features will be added in the app/modules/items directory.
We will create a service to manage access to the API.

The steps are as follows.

  • Create a service via an items.service.ts file
  • Edit the items.service.ts file to access the api
  • Edit the items.component.ts file to call the service
  • Edit the items.module.ts file to call the necessary Angular module
  • Edit the items.component.spec.ts and items.service.spec.ts test files
  • Modify the items.component.html file to personalize the display of the result
  • Modify the home.component.ts file to allow the link with httpclient

Let's start by creating the service with the corresponding angular-cli command and then modify the files mentioned.

# Creation of the items service
ng generate service modules/application/example-items/items --flat

# Creating the items service (Method 2)
ng g s modules/application/example-items/items --flat
src/app/modules/application/example-items/items.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

const httpOptions = {
  headers: new HttpHeaders(
    {
      'Content-Type': 'application/json',
    }
  )
};

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

  constructor(private http: HttpClient) { }

  getItems(url: string): Observable<object> {
    return this.http.get(url, httpOptions);
  }

}
src/app/modules/application/example-items/items.component.ts
import { Component, OnInit } from '@angular/core';
import { ItemsService } from './items.service';

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

  items: any;
  loaded: boolean;
  constructor(
    private ItemsService: ItemsService) {
    this.loaded = false;
  }

  ngOnInit(): void {
    this.getUsers();
  }

  getUsers(): void {
    this.loaded = false;
    this.ItemsService.getItems('https://jsonplaceholder.typicode.com/users')
      .subscribe(
        items => {
          this.items = items;
          this.loaded = true;
        });
  }

  resetUsers(): void {
    this.items = null;
    this.loaded = true;
  }

}
src/app/modules/application/example-items/items.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ItemsComponent } from './items.component';
import { ItemsRoutingModule } from './items-routing.module';
import { HttpClientModule } from '@angular/common/http';
import { ItemsService } from './items.service';

@NgModule({
  imports: [
    CommonModule,
    ItemsRoutingModule,
    HttpClientModule
  ],
  exports: [
    ItemsComponent
  ],
  declarations: [
    ItemsComponent
  ],
  providers: [ItemsService
  ],
})
export class ItemsModule { }

src/app/modules/application/example-items/items.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { ItemsComponent } from './items.component';

describe('ItemsComponent', () => {
  let component: ItemsComponent;
  let fixture: ComponentFixture<ItemsComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
        HttpClientModule
      ],
      declarations: [ItemsComponent]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(ItemsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
src/app/modules/example-items/items.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { ItemsService } from './items.service';

describe('ItemsService', () => {
  let service: ItemsService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientModule
      ]
    });
    service = TestBed.inject(ItemsService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});
src/app/modules/example-items/items.component.html
<div class="row pb-4">

    <div class="col-12 col-sm-12 col-md-3 col-lg-3 col-xl-3">
      <div class="row pb-4">
        <div class="card" style="width: 18rem;">
          <div class="card-body">
            <h5 class="card-title">Feature : HttpClient</h5>
            <hr>
            <p class="card-text">Use HtppClient</p>
            <button type="button" class="btn btn-primary mr-4" (click)="getUsers()">Get</button>
            <button type="button" class="btn btn-primary" (click)="resetUsers()">Reset</button>
          </div>
        </div>
        <div *ngIf="!loaded">
          <div class="spinner-grow text-primary" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-secondary" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-success" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-danger" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-warning" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-info" role="status">
            <span class="sr-only">Loading...</span>
          </div>
          <div class="spinner-grow text-dark" role="status">
            <span class="sr-only">Loading...</span>
          </div>
        </div>
      </div>
    </div>
  
    <div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-8">
      <div class="row">
        <div class="table-responsive httpclient-table blue-gradient" *ngIf="loaded">
          <table class="table table-hover table-striped table-responsive-md">
            <thead>
              <tr>
                <th scope="col">name</th>
                <th scope="col">username</th>
                <th scope="col">email</th>
              </tr>
            </thead>
            <tbody>
              <tr *ngFor="let item of items">
                <td>{{item.name}}</td>
                <td>{{item.username}}</td>
                <td>{{item.email}}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  
  </div>
items.component.css
.httpclient-table {
  border-radius: 10px;
}

.httpclient-table table {
  color: #FFFFFF;
}

.httpclient-table.blue-gradient {
  background: linear-gradient(40deg, #45cafc, #5664bd) !important
}
src/app/modules/general/home/home.component.ts
        {
          name: 'Items',
          description: 'Items',
          icon: 'fab fa-bootstrap',
          link: 'httpclient'
        },

Errors

Noticed
Depending on the versions of Angular an error may occur.


Let's perform an ssr compilation

  • npm run build:ssr
  • npm run serve:ssr

If we type the following address

  • http://localhost:4000/httpclient

we get an error on the server

  • ReferenceError: XMLHttpRequest is not defined

To resolve this error, we need to move httpclientmodule from items.module.ts to app.module.ts

src/app/modules/application/example-items/items.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ItemsComponent } from './items.component';
import { ItemsRoutingModule } from './items-routing.module';
import { ItemsService } from './items.service';

@NgModule({
  imports: [
    CommonModule,
    ItemsRoutingModule,
  ],
  exports: [
    ItemsComponent
  ],
  declarations: [
    ItemsComponent
  ],
  providers: [ItemsService],
})
export class ItemsModule { }
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, isDevMode } from '@angular/core';

import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
import { ServiceWorkerModule } from '@angular/service-worker';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    NotFoundComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: !isDevMode(),
      // Register the ServiceWorker as soon as the application is stable
      // or after 30 seconds (whichever comes first).
      registrationStrategy: 'registerWhenStable:30000'
    }),
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Conclusion

All that remains is to test the different Angular scripts.

# Développement
npm run start
http://localhost:4200/

# Tests
npm run lint
npm run test

# AOT compilation
npm run build

# SSR compilation
npm run build:ssr
npm run serve:ssr
http://localhost:4000/

Source Code

The source code used at the start of the tutorial is available on github
https://github.com/ganatan/angular-pwa

The source code obtained at the end of this tutorial is available on github
https://github.com/ganatan/angular-httpclient


The last step allows you to obtain an example application


The source code for this final application is available on GitHub
https://github.com/ganatan/angular-app