HttpClient avec Angular 8

14/11/19 dannyEnglish Version

Qu’allons nous faire ?

Nous allons rajouter dans notre projet le mécanisme HttpClient pour communiquer avec un serveur HTTP.
Nous utiliserons le framework javascript Angular version 8.2.14

Il s'agit de l'étape 6 de notre guide Angular qui nous permettra d'obtenir une Application Web de type PWA.

Le projet Angular de base que nous utiliserons dispose déjà des caractéristiques suivantes

  • Généré avec Angular CLI
  • Le Routing
  • Le Lazy Loading
  • Le framework CSS Bootstrap
  • Server Side Rendering

Tous les sources créés sont indiqués en fin de tutoriel.

L' application est à l'adresse suivante 


Installation

Notre application Angular représente notre partie Frontend.
Nous allons utiliser le module Httpclient d’angular pour accèder à une API REST qui représentera notre partie Backend.

L’objet de ce tutoriel n’étant pas de développer un backend, nous allons donc choisir arbitrairement une API disponible sur le web.

JSONPlaceholder est une API REST gratuite idéale pour les tutoriels.
Elle est accessible avec le lien suivant https://jsonplaceholder.typicode.com/

Les étapes seront les suivantes

  • Créer un module items
  • Modifier l'interface visuelle du frontend
  • Effectuer le routage
  • Adapter le module pour faire appel à l'API REST


Commençons par créer notre module Items, celui-ci étant spécifique à notre application sera créé dans le répertoire modules/application.

# Création du module items
ng generate component modules/application/items --module=app
ng generate module modules/application/items --routing --module=app

# Création du module items (Méthode 2)
ng g c modules/application/items --module=app
ng g m modules/application/items --routing --module=app

Le répertoire modules/application est automatiquement créé par Angular CLI

Modifions les fichiers suivants

  • 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
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';

const routes: Routes = [
  { path: '', component: HomeComponent, },
  {
    path: 'items',
    loadChildren: () => import('./modules/application/items/items.module').then(mod => mod.ItemsModule)
  },
  {
    path: 'about',
    loadChildren: () => import('./modules/general/about/about.module').then(mod => mod.AboutModule)
  },
  {
    path: 'contact',
    loadChildren: () => import('./modules/general/contact/contact.module').then(mod => mod.ContactModule)
  },
  { path: '**', component: NotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  declarations: []
})
export class AppRoutingModule { }
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } 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';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    NotFoundComponent,
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'angular-starter' }),
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
src/app/modules/general/home/home.component.html
<div class="row">
  <div class="col-md-12 text-center">
    <div class="row pt-1 mb-2">
      <div class="col-md-4 text-center mb-2">
        <h1 class="h5">
          <i class="fab fa-android fa-lg mr-2 text-primary"></i>
          {{ name }}<i class="fab fa-apple fa-lg ml-2 mr-1 text-primary"></i>
        </h1>
      </div>
      <div class="col-md-4 text-center">
        <h2 class="h5">{{angular}}&nbsp;&nbsp;<img src="./assets/params/images/logo/angular.png" width="36" height="36"
            alt=""></h2>
      </div>
      <div class="col-md-4 text-center">
        <h2 class="h5">{{bootstrap}}&nbsp;&nbsp;<img src="./assets/params/images/logo/bootstrap.png" width="28"
            height="28" alt=""></h2>
      </div>
    </div>
    <hr>
    <div class="row mb-1">
      <div class="col-md-12 text-center">
        <h3 class="h6">Features&nbsp;&nbsp;<i class="fas fa-list"></i></h3>
      </div>
    </div>
    <div class="card" style="width: 18rem;">
      <div class="card-body">
        <h5 class="card-title">Items</h5>
        <p class="card-text">Test HttpClient</p>
        <a href="#" routerLink="/items" class="btn btn-primary">Items</a>
      </div>
    </div>
  </div>
</div>
src/app/modules/general/home/home.component.css
.card {
    display: block;
    background-color: rgba(255, 255, 255, .8);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);
    border-radius: 2px;
    transition: all .2s ease-in-out;
  }
  
  .card:hover {
    box-shadow: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);
  }
src/app/modules/application/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/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 { }

A ce stade nous pouvons tester le script de debugage pour contrôler l'architecture de notre projet.
npm run start


Integration

Il ne reste plus qu'à intégrer la gestion httpclient sur notre partie Items.

Pour respecter les best practices angular toutes les fonctionnalités seront ajoutées dans le répertoire app/modules/items.
Nous créerons un service pour gérer l'accès à l'api.

Les étapes sont les suivantes.

  • Créer un service via un fichier items.service.ts
  • Modifier le fichier items.service.ts pour accèder à l’api
  • Modifier le fichier items.component.ts pour appeler le service
  • Modifier le fichier items.module.ts pour appeler le module angular nécessaire
  • Modifier les fichiers de test items.component.spec.ts et  items.service.spec.ts
  • Modifier le fichier items.component.html pour personaliser l'affichage du résultat

Commencons par créer le service avec la commande angular-cli correspondante puis modifions les fichiers cités.


# Création du service items
ng generate service modules/application/items/items

# Création du service items (Méthode 2)
ng g s modules/application/items/items
src/app/modules/application/items/items.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

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

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

  constructor(private http: HttpClient) { }

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

}
src/app/modules/application/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;
  constructor(
    private itemsService: ItemsService) {

  }

  ngOnInit() {
    this.getUsers();
  }

  getUsers() {
    this.itemsService.getItems('https://jsonplaceholder.typicode.com/users')
      .subscribe(
        items => {
          this.items = items;
        });
  }

}
src/app/modules/application/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/items/items.component.spec.ts
import { async, 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(() => {
    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/items/items.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';

import { ItemsService } from './items.service';

describe('ItemsService', () => {
  beforeEach(() => TestBed.configureTestingModule({
    imports: [
      HttpClientModule
    ],
  }));

  it('should be created', () => {
    const service: ItemsService = TestBed.get(ItemsService);
    expect(service).toBeTruthy();
  });
});
src/app/modules/items/items.component.html
<div class="container">

  <div class="row mt-4">
    <div class="col">
      <h2>
        Module Items
      </h2>
    </div>
  </div>

  <div class="row mt-4">
    <div class="col-sm mb-2">
      <button type="button" class="btn btn-primary" (click)="getUsers()">Get Users</button>
    </div>
  </div>

  <div class="row mt-4">
    <div class="col-12">
      <div class="table-responsive">
        <table class="table table-sm table-hover table-bordered table-striped">
          <thead class="thead-dark">
            <tr>
              <th scope="col">name</th>
              <th scope="col">email</th>
            </tr>
          </thead>
          <tbody>
            <tr *ngFor="let item of items; let i=index">
              <td>{{item.name}}</td>
              <td>{{item.email}}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>

</div>

Erreur

Effectuons une compilation ssr

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

Si nous tapons l'adresse suivante 

  • http://localhost:4000/items

nous obtenons une erreur sur le serveur

  • ReferenceError: XMLHttpRequest is not defined

Pour résoudre cette errreur , Il nous faut deplacer httpclientmodule de items.module.ts vers app.module.ts

src/app/modules/application/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 } 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 { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    NotFoundComponent,
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'angular-starter' }),
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Conclusion

Il ne reste plus qu'à tester les différents scripts Angular.


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

# Tests
npm run lint
npm run test
npm run e2e

# AOT compilation
npm run build

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

Laissez un commentaire

Votre avis
Cette adresse ne sera pas publiée