Ajouter la communication HTTP à votre application

Ce tutoriel montre comment intégrer HTTP et une API dans votre application.

Jusqu'à présent, votre application lisait les données depuis un tableau statique dans un service Angular. L'étape suivante consiste à utiliser un serveur JSON avec lequel votre application communiquera via HTTP. La requête HTTP simulera l'expérience de travailler avec des données provenant d'un serveur.

Important : Nous vous recommandons d'utiliser votre environnement local pour cette étape du tutoriel.

Ce que vous allez apprendre

Votre application utilisera les données d'un serveur JSON

  1. Configurer le serveur JSON

    JSON Server est un outil open source utilisé pour créer des API REST simulées. Vous l'utiliserez pour servir les données de logements actuellement stockées dans le service de logements.

    1. Installez json-server depuis npm à l'aide de la commande suivante.

      npm install -g json-server
    2. Dans le répertoire racine de votre projet, créez un fichier nommé db.json. C'est là que vous stockerez les données pour json-server.

    3. Ouvrez db.json et copiez-y le code suivant

      {
        "locations": [
          {
            "id": 0,
            "name": "Acme Fresh Start Housing",
            "city": "Chicago",
            "state": "IL",
            "photo": "https://angular.dev/assets/images/tutorials/common/bernard-hermant-CLKGGwIBTaY-unsplash.jpg",
            "availableUnits": 4,
            "wifi": true,
            "laundry": true
          },
          {
            "id": 1,
            "name": "A113 Transitional Housing",
            "city": "Santa Monica",
            "state": "CA",
            "photo": "https://angular.dev/assets/images/tutorials/common/brandon-griggs-wR11KBaB86U-unsplash.jpg",
            "availableUnits": 0,
            "wifi": false,
            "laundry": true
          },
          {
            "id": 2,
            "name": "Warm Beds Housing Support",
            "city": "Juneau",
            "state": "AK",
            "photo": "https://angular.dev/assets/images/tutorials/common/i-do-nothing-but-love-lAyXdl1-Wmc-unsplash.jpg",
            "availableUnits": 1,
            "wifi": false,
            "laundry": false
          },
          {
            "id": 3,
            "name": "Homesteady Housing",
            "city": "Chicago",
            "state": "IL",
            "photo": "https://angular.dev/assets/images/tutorials/common/ian-macdonald-W8z6aiwfi1E-unsplash.jpg",
            "availableUnits": 1,
            "wifi": true,
            "laundry": false
          },
          {
            "id": 4,
            "name": "Happy Homes Group",
            "city": "Gary",
            "state": "IN",
            "photo": "https://angular.dev/assets/images/tutorials/common/krzysztof-hepner-978RAXoXnH4-unsplash.jpg",
            "availableUnits": 1,
            "wifi": true,
            "laundry": false
          },
          {
            "id": 5,
            "name": "Hopeful Apartment Group",
            "city": "Oakland",
            "state": "CA",
            "photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-JvQ0Q5IkeMM-unsplash.jpg",
            "availableUnits": 2,
            "wifi": true,
            "laundry": true
          },
          {
            "id": 6,
            "name": "Seriously Safe Towns",
            "city": "Oakland",
            "state": "CA",
            "photo": "https://angular.dev/assets/images/tutorials/common/phil-hearing-IYfp2Ixe9nM-unsplash.jpg",
            "availableUnits": 5,
            "wifi": true,
            "laundry": true
          },
          {
            "id": 7,
            "name": "Hopeful Housing Solutions",
            "city": "Oakland",
            "state": "CA",
            "photo": "https://angular.dev/assets/images/tutorials/common/r-architecture-GGupkreKwxA-unsplash.jpg",
            "availableUnits": 2,
            "wifi": true,
            "laundry": true
          },
          {
            "id": 8,
            "name": "Seriously Safe Towns",
            "city": "Oakland",
            "state": "CA",
            "photo": "https://angular.dev/assets/images/tutorials/common/saru-robert-9rP3mxf8qWI-unsplash.jpg",
            "availableUnits": 10,
            "wifi": false,
            "laundry": false
          },
          {
            "id": 9,
            "name": "Capital Safe Towns",
            "city": "Portland",
            "state": "OR",
            "photo": "https://angular.dev/assets/images/tutorials/common/webaliser-_TPTXZd9mOo-unsplash.jpg",
            "availableUnits": 6,
            "wifi": true,
            "laundry": true
          }
        ]
      }
    4. Enregistrez ce fichier.

    5. Il est temps de tester votre configuration. Depuis la ligne de commande, à la racine de votre projet, exécutez les commandes suivantes.

      json-server --watch db.json
    6. Dans votre navigateur web, accédez à http://localhost:3000/locations et vérifiez que la réponse contient les données stockées dans db.json.

    Si vous rencontrez des difficultés avec votre configuration, vous trouverez plus de détails dans la documentation officielle.

  2. Modifier le service pour utiliser un serveur web au lieu d'un tableau local

    La source de données est configurée ; l'étape suivante consiste à modifier votre application web pour s'y connecter et utiliser les données.

    1. Dans src/app/housing.service.ts, apportez les modifications suivantes :

    2. Modifiez le code pour supprimer la propriété housingLocationList et le tableau contenant les données, ainsi que la propriété baseUrl.

    3. Ajoutez une propriété de type chaîne nommée url et affectez-lui la valeur 'http://localhost:3000/locations'

      Ajouter la propriété url dans housing.service.ts

      import {Service} from '@angular/core';
      import {HousingLocationInfo} from './housinglocation';
      
      @Service()
      export class HousingService {
        url = 'http://localhost:3000/locations';
      
        async getAllHousingLocations(): Promise<HousingLocationInfo[]> {
          const data = await fetch(this.url);
          return (await data.json()) ?? [];
        }
      
        async getHousingLocationById(id: number): Promise<HousingLocationInfo | undefined> {
          const data = await fetch(`${this.url}/${id}`);
          const locationJson = await data.json();
          return locationJson ?? {};
        }
      
        submitApplication(firstName: string, lastName: string, email: string) {
          // tslint:disable-next-line
          console.log(firstName, lastName, email);
        }
      }

      Ce code provoquera des erreurs dans le reste du fichier, car celui-ci dépend de la propriété housingLocationList. Nous allons ensuite modifier les méthodes du service.

    4. Modifiez la fonction getAllHousingLocations pour qu'elle appelle le serveur web que vous avez configuré.

      Modifier la méthode getAllHousingLocations dans housing.service.ts

      import {Service} from '@angular/core';
      import {HousingLocationInfo} from './housinglocation';
      
      @Service()
      export class HousingService {
        url = 'http://localhost:3000/locations';
      
        async getAllHousingLocations(): Promise<HousingLocationInfo[]> {
          const data = await fetch(this.url);
          return (await data.json()) ?? [];
        }
      
        async getHousingLocationById(id: number): Promise<HousingLocationInfo | undefined> {
          const data = await fetch(`${this.url}/${id}`);
          const locationJson = await data.json();
          return locationJson ?? {};
        }
      
        submitApplication(firstName: string, lastName: string, email: string) {
          // tslint:disable-next-line
          console.log(firstName, lastName, email);
        }
      }

      Le code utilise maintenant du code asynchrone pour effectuer une requête GET via HTTP.

      Utile : Dans cet exemple, le code utilise fetch. Pour des cas d'usage plus avancés, envisagez d'utiliser le HttpClient fourni par Angular.

    5. Modifiez la fonction getHousingLocationById pour qu'elle appelle le serveur web que vous avez configuré.

      Utile : Remarquez que l'URL de fetch ajoute maintenant l'id comme segment de chemin (${this.url}/${id}) pour demander le logement dont la propriété id a la valeur correspondante.

      Modifier la méthode getHousingLocationById dans housing.service.ts

      import {Service} from '@angular/core';
      import {HousingLocationInfo} from './housinglocation';
      
      @Service()
      export class HousingService {
        url = 'http://localhost:3000/locations';
      
        async getAllHousingLocations(): Promise<HousingLocationInfo[]> {
          const data = await fetch(this.url);
          return (await data.json()) ?? [];
        }
      
        async getHousingLocationById(id: number): Promise<HousingLocationInfo | undefined> {
          const data = await fetch(`${this.url}/${id}`);
          const locationJson = await data.json();
          return locationJson ?? {};
        }
      
        submitApplication(firstName: string, lastName: string, email: string) {
          // tslint:disable-next-line
          console.log(firstName, lastName, email);
        }
      }
    6. Une fois toutes les modifications terminées, votre service modifié doit correspondre au code suivant.

      Version finale de housing.service.ts

      import {Service} from '@angular/core';
      import {HousingLocationInfo} from './housinglocation';
      
      @Service()
      export class HousingService {
        url = 'http://localhost:3000/locations';
      
        async getAllHousingLocations(): Promise<HousingLocationInfo[]> {
          const data = await fetch(this.url);
          return (await data.json()) ?? [];
        }
      
        async getHousingLocationById(id: number): Promise<HousingLocationInfo | undefined> {
          const data = await fetch(`${this.url}/${id}`);
          const locationJson = await data.json();
          return locationJson ?? {};
        }
      
        submitApplication(firstName: string, lastName: string, email: string) {
          // tslint:disable-next-line
          console.log(firstName, lastName, email);
        }
      }
  3. Modifier les composants pour utiliser des appels asynchrones au service de logements

    Le serveur lit maintenant les données à partir de la requête HTTP, mais les composants qui dépendent du service comportent désormais des erreurs, car ils ont été programmés pour utiliser la version synchrone du service.

    1. Dans src/app/home/home.ts, modifiez le constructor pour utiliser la nouvelle version asynchrone de la méthode getAllHousingLocations. Comme nous n'avons pas utilisé de signals pour notre état, vous devez notifier Angular qu'un changement nécessitant une synchronisation s'est produit. Appelez this.changeDetectorRef.markForCheck() pour cela.

      Modifier le constructor dans home.ts

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {HousingLocation} from '../housing-location/housing-location';
      import {HousingLocationInfo} from '../housinglocation';
      import {HousingService} from '../housing.service';
      
      @Component({
        selector: 'app-home',
        imports: [HousingLocation],
        template: `
          <section>
            <form>
              <input type="text" placeholder="Filter by city" #filter />
              <button class="primary" type="button" (click)="filterResults(filter.value)">Search</button>
            </form>
          </section>
          <section class="results">
            @for (housingLocation of filteredLocationList; track $index) {
              <app-housing-location [housingLocation]="housingLocation" />
            }
          </section>
        `,
        styleUrls: ['./home.css'],
      })
      export class Home {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        housingLocationList: HousingLocationInfo[] = [];
        housingService: HousingService = inject(HousingService);
        filteredLocationList: HousingLocationInfo[] = [];
      
        constructor() {
          this.housingService
            .getAllHousingLocations()
            .then((housingLocationList: HousingLocationInfo[]) => {
              this.housingLocationList = housingLocationList;
              this.filteredLocationList = housingLocationList;
              this.changeDetectorRef.markForCheck();
            });
        }
      
        filterResults(text: string) {
          if (!text) {
            this.filteredLocationList = this.housingLocationList;
            return;
          }
      
          this.filteredLocationList = this.housingLocationList.filter((housingLocation) =>
            housingLocation?.city.toLowerCase().includes(text.toLowerCase()),
          );
        }
      }
    2. Dans src/app/details/details.ts, modifiez le constructor pour utiliser la nouvelle version asynchrone de la méthode getHousingLocationById. Comme précédemment, vous devez aussi appeler this.changeDetectorRef.markForCheck() pour notifier Angular des changements.

      Modifier le constructor dans details.ts

      import {ChangeDetectorRef, Component, inject} from '@angular/core';
      import {ActivatedRoute} from '@angular/router';
      import {HousingService} from '../housing.service';
      import {HousingLocationInfo} from '../housinglocation';
      import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
      
      @Component({
        selector: 'app-details',
        imports: [ReactiveFormsModule],
        template: `
          <article>
            <img
              class="listing-photo"
              [src]="housingLocation?.photo"
              alt="Exterior photo of {{ housingLocation?.name }}"
              crossorigin
            />
            <section class="listing-description">
              <h2 class="listing-heading">{{ housingLocation?.name }}</h2>
              <p class="listing-location">{{ housingLocation?.city }}, {{ housingLocation?.state }}</p>
            </section>
            <section class="listing-features">
              <h2 class="section-heading">About this housing location</h2>
              <ul>
                <li>Units available: {{ housingLocation?.availableUnits }}</li>
                <li>Does this location have wifi: {{ housingLocation?.wifi }}</li>
                <li>Does this location have laundry: {{ housingLocation?.laundry }}</li>
              </ul>
            </section>
            <section class="listing-apply">
              <h2 class="section-heading">Apply now to live here</h2>
              <form [formGroup]="applyForm" (submit)="submitApplication()">
                <label for="first-name">First Name</label>
                <input id="first-name" type="text" formControlName="firstName" />
      
                <label for="last-name">Last Name</label>
                <input id="last-name" type="text" formControlName="lastName" />
      
                <label for="email">Email</label>
                <input id="email" type="email" formControlName="email" />
                <button type="submit" class="primary">Apply now</button>
              </form>
            </section>
          </article>
        `,
        styleUrls: ['./details.css'],
      })
      export class Details {
        private readonly changeDetectorRef = inject(ChangeDetectorRef);
        route: ActivatedRoute = inject(ActivatedRoute);
        housingService = inject(HousingService);
        housingLocation: HousingLocationInfo | undefined;
      
        applyForm = new FormGroup({
          firstName: new FormControl(''),
          lastName: new FormControl(''),
          email: new FormControl(''),
        });
      
        constructor() {
          const housingLocationId = parseInt(this.route.snapshot.params['id'], 10);
          this.housingService.getHousingLocationById(housingLocationId).then((housingLocation) => {
            this.housingLocation = housingLocation;
            this.changeDetectorRef.markForCheck();
          });
        }
      
        submitApplication() {
          this.housingService.submitApplication(
            this.applyForm.value.firstName ?? '',
            this.applyForm.value.lastName ?? '',
            this.applyForm.value.email ?? '',
          );
        }
      }
    3. Enregistrez votre code.

    4. Ouvrez l'application dans le navigateur et vérifiez qu'elle s'exécute sans erreur.

Note : Cette leçon s'appuie sur l'API fetch du navigateur. Pour la prise en charge des intercepteurs, consultez la documentation du client HTTP

Résumé : Dans cette leçon, vous avez modifié votre application pour utiliser un serveur web local (json-server) et des méthodes de service asynchrones pour récupérer des données.

Félicitations ! Vous avez terminé ce tutoriel avec succès et vous êtes prêt à poursuivre votre parcours en construisant des applications Angular encore plus complexes.

Si vous souhaitez en apprendre davantage, envisagez de suivre d'autres tutoriels et guides Angular destinés aux développeurs.