Une application web dynamique aura besoin de communiquer avec un serveur, que ce soit pour récupérer, créer, modifier ou supprimer des données. Avec Angular, ces requêtes sont gérées avec HttpClient.
Jusqu'ici, votre application FaceSnaps possède toutes ses données en dur dans un service. Dans ce chapitre, vous allez utiliser un backend de développement simple pour rendre votre application totalement dynamique.
Installez et lancez le backend
Vous pouvez cloner le backend depuis le repository Github du cours, angular-intermediate-backend.
Une fois cloné, dans le dossier du backend, faites npm install
, puis npm run start
. Vous devriez voir quelque chose comme :
… suivi d'une liste de ressources disponibles. Ça y est : le backend de développement tourne sur http://localhost:3000 !
Créez votre première requête
Pour pouvoir utiliser HttpClient dans votre application, vous devez ajouter HttpClientModule aux imports de votre AppModule :
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule
],
Cet import vous permet d'injecter HttpClient là où vous en avez besoin.
… mais où en avez-vous besoin ? À quel niveau de votre application auriez-vous besoin de créer et envoyer des requêtes HTTP ?
Eh bien dans FaceSnapsService ! Le service centralise toutes les interactions avec les données, donc c'est bien là que vous allez gérer les communications avec le serveur.
Injectez HttpClient dans FaceSnapsService en y créant un constructor, comme pour les components :
export class FaceSnapsService {
constructor(private http: HttpClient) {}
// ...
}
Comme vous l'avez appris lors de la première partie de ce cours, les Observables existent pour faciliter la gestion de l'asynchrone. Les requêtes HTTP étant asynchrones, le HttpClient génère les requêtes sous forme d'Observables ! C'est plutôt pratique, non ?
Le fait que les requêtes HTTP soient des Observables entraîne un changement de logique quant à l'implémentation des différentes parties de l'application. Par exemple, vous récupérez actuellement un tableau statique dans FaceSnapListComponent. Il faudra modifier cette implémentation pour utiliser un Observable qui émet ce tableau.
Dans FaceSnapListComponent, ajoutez un Observable faceSnaps$
qui émet des tableaux de FaceSnap, et retirez l'ancienne logique :
export class FaceSnapListComponent implements OnInit {
faceSnaps$!: Observable<FaceSnap[]>;
constructor(private faceSnapsService: FaceSnapsService) { }
ngOnInit(): void {}
}
Maintenant, dans le template :
<app-face-snap *ngFor="let faceSnap of faceSnaps$ | async" [faceSnap]="faceSnap"></app-face-snap>
Comme vous pouvez le constater, le reste de l'implémentation ne change pas : on utilise simplement le pipe async
pour subscribe à notre nouvel Observable – on continue à utiliser *ngFor
, car l'Observable émettra des tableaux.
Bien sûr, rien n'est émis encore, donc l'application reste vide !
Passons du côté du service pour modifier la méthode getAllFaceSnaps
. Elle ne retournera plus un tableau FaceSnap[]
, mais un Observable<FaceSnap[]>
, et vous allez utiliser la méthode get
de HttpClient :
getAllFaceSnaps(): Observable<FaceSnap[]> {
return this.http.get<FaceSnap[]>('http://localhost:3000/facesnaps');
}
La méthode get
, comme son nom l'indique, génère une requête GET à l'URL que vous lui passez. Vous devez spécifier le type de la réponse – il s'agit ici d'un tableau de FaceSnap.
De retour dans FaceSnapListComponent, appelez la méthode que vous venez de modifier pour initialiser faceSnaps$
:
ngOnInit(): void {
this.faceSnaps$ = this.faceSnapsService.getAllFaceSnaps();
}
Et là, comme par magie :
Le pipe async
souscrit à l'Observable, qui envoie la requête et qui émet la réponse : *ngFor
récupère ensuite cette émission et l'affiche comme il le ferait avec n'importe quel tableau !
Récupérez un seul FaceSnap
Continuons la "dynamisation" de votre application en modifiant SingleFaceSnapComponent.
Dans le component, ajoutez un Observable de type FaceSnap :
export class SingleFaceSnapComponent implements OnInit {
faceSnap!: FaceSnap;
faceSnap$!: Observable<FaceSnap>;
buttonText!: string;
// ...
}
Modifiez dès maintenant le template pour consommer cet Observable en utilisant la syntaxe *ngIf
pour y souscrire et pour créer un alias :
<div class="face-snap-card" *ngIf="faceSnap$ | async as faceSnap" [ngClass]="{ snapped: buttonText === 'Oops, unSnap!' }">
<!-- ... -->
</div>
Ensuite, vous allez modifier l'implémentation de la méthode de service getFaceSnapById
. Le backend de développement propose une route pour récupérer un FaceSnap par son id
:
getFaceSnapById(faceSnapId: number): Observable<FaceSnap> {
return this.http.get<FaceSnap>(`http://localhost:3000/facesnaps/${faceSnapId}`);
}
Tout simple, finalement !
De retour dans le component, initialisez l'Observable :
ngOnInit() {
this.buttonText = 'Oh Snap!';
const faceSnapId = +this.route.snapshot.params['id'];
this.faceSnap$ = this.faceSnapsService.getFaceSnapById(faceSnapId);
}
Ainsi, si vous cliquez sur le bouton View d'un FaceSnap :
Super !
Mais il reste un détail important : on ne peut plus "snap" !
Comme toujours, il y a plusieurs approches possibles pour réimplémenter cette fonctionnalité. Celle que je veux vous montrer nécessite une requête composée, et ces requêtes sont le sujet du prochain chapitre !
En résumé
Importez HttpClientModule et injectez HttpClient pour débloquer les requêtes HTTP.
Les requêtes HTTP en Angular sont des Observables qui :
émettent une fois et complètent lors d'une réponse positive ;
émettent une erreur (et sont donc détruits) lors d'une erreur serveur.
Les Observables générés par HttpClient envoient leur requête uniquement lorsqu'on souscrit à ces Observables ;
Souscrire à l'Observable HTTP avec le pipe
async
permet d'afficher facilement les données retournées par un serveur ;La méthode
get
de HttpClient crée une requête GET à l'URL passée en argument.
Dans le prochain chapitre, vous créerez des requêtes composées, où la réponse d'une requête est utilisée automatiquement pour en déclencher une autre !