Aller au contenu

La page Accueil

Maintenant que nous avons une jolie navbar, nous pouvons coder une belle page "Accueil" 🏠
On veut quelque chose qui ressemble à ça.


Création du HomeComponent

Comme pour la génération du NavbarComponant, tu peux générer un nouveau composant pour la page "Accueil" avec Angular CLI !

ng g c home
Tu peux ensuite l'afficher depuis le fichier app.component.html via la valeur du selector.
<app-navbar [title]="title"></app-navbar>
<app-home></app-home>

Attention

N'oublie pas les imports !!

Tu dois avoir home works! qui s'affiche sur ta page !


Appel au backend

Les données à afficher sur la page viennent du backend que tu as lancé au moment des installations.

Info

Un backend et un frontend communiquent généralement via des appels HTTP.

Tous les appels que tu peux faire à ce backend (codé pour toi) sont définis sur sa documentation

🚀 Notre objectif est donc de récupérer tous les films via un appel HTTP au backend et de les afficher sur notre page


1. Création du modèle

Pour récupérer tous les films nous utilisons cet endpoint qui est un appel GET.
Il retourne une liste d'objets Movie de cette forme :

  {
    "id": 0,
    "title": "string",
    "director": "string",
    "rate": 0,
    "releaseDate": "2024-05-27T14:53:27.038Z",
    "synopsis": "string",
    "image": "string"
  }
Tu peux aller regarder directement sur http://localhost:8080/movies pour voir ce que cette requête GET retourne.

Côté front, nous devons créer un objet qui ressemble à ce Movie pour pouvoir récupérer les données au moment de l'appel.

C'est parti :

  • Clic-droit sur le dossier app et crée un dossier que tu appelles models
  • Clic-droit sur ce dossier models et crée un ficher que tu appelles movie.ts
  • Dans ce fichier, crée l'objet qui représente les films en se basant sur le modèle du backend :
export interface Movie {
  id?: number;
  title: string;
  releaseDate: Date;
  director: string;
  rate?: number;
  synopsis: string;
  image?: string;
}

Info

On utilise export pour pouvoir utiliser cet objet dans d'autres fichiers
On fait une interface qui correspond au modèle du backend (avec les mêmes noms de champs et des types adaptés)


2. Création d'un service pour appeler le backend

Nous avons besoin d'un service que nous pourrons appeler dans nos composants pour requêter notre backend.

La commande pour générer un service est très semblable à celle qui nous permet de générer un composant :

 ng generate service services/movies

Info

Le services/ permet de préciser un dossier dans lequel mettre le service. Si le dossier n'existe pas, il le crée.

Pour faire des appels HTTP à notre backend, nous avons besoin d'un service fourni par Angular qu'il faut provider de manière globale : le HttpClient.

Pour ça, ajoute dans la liste des providers du app.config.ts : provideHttpClient()
Tu dois donc avoir :

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideHttpClient()]
};

Maintenant, il faut faire l'appel à notre backend :

  • Rends-toi dans le fichier créé movies.service.ts
  • Supprime le constructor
  • Injecte l'HttpClient dans la classe en déclarant une variable : private readonly httpClient = inject(HttpClient)
  • Déclare l'url de ton backend sur laquelle se trouve les données que tu veux récupérer : private readonly url = "http://localhost:8080/movies"
  • Crée une méthode dans la classe qui fait l'appel GET :
getMovies(): Observable<Movie[]> {
    return this.httpClient.get<Movie[]>(this.url);
  }

Info

Comme nous récupérons des données via un appel GET, nous utilisons la méthode get de l'HttpClient.
Notre appel récupère une liste de Movie que l'on doit préciser au niveau de la méthode.
Les méthodes de l'HttpClient nous retournent toujours un Observable. Un Observable est un objet qui permet de gérer des flux de données asynchrones : on doit subscribe pour récupérer son contenu.

Bravo, ton appel est créé !!


3. Appel du service dans le composant

Maintenant que ton appel au backend est accessible depuis la méthode getMovies() dans un service, tu dois l'appeler dans ton composant.

Pour cela :

  • Rends-toi dans le fichier home.component.ts
  • Injecte ton service en créant une variable private readonly moviesService = inject(MoviesService)
  • Crée une variable pour récupérer ta liste de Movie : movies$: Observable<Movie[]> = this.moviesService.getMovies()

Info

Comme vu un peu plus haut, on doit subscribe pour réellement trigger l'appel HTTP et récupérer le contenu d'un observable. On peut le faire de deux façons :
Soit en utilisant le .subscribe() comme ça :

movies: Movie[] = []
this.moviesService.getMovies.subscribe(moviesFromBackend => this.movies = moviesFromBackend)
Soit en utilisant directement le Pipe | async dans une directive (@if ou @for)côté HTML :
@for (movie of movies$ | async; track movie.id)
Si c'est possible, il faut choisir cette façon qui est plus performante.

Dans notre cas, on utilise bien le | async dans le home.component.html. Une fois qu'on a récupéré notre liste de films, on boucle sur la liste avec un @for et on affiche les films un par un :

@for (movie of movies$ | async; track movie.id) {
  <p>{{movie.id}}</p>
  <p>{{movie.title}}</p>
  <p>{{movie.releaseDate | date:'dd/MM/yyyy'}}</p>
  <p>{{movie.director}}</p>
  <p>{{movie.rate}}</p>
  <p>{{movie.synopsis}}</p>
}

Info

Le track permet à Angular d'identifier l'item de façon unique dans le DOM pour gagner en performance lors de modification de la liste.
Le pipe | date te permet de formater ta date selon un format (le backend nous renvoie un format de Date classique et ce n'est pas affichable tel quel).
Tu as la documentation.
⚠️ Attention, il faut penser à importer DatePipe et AsyncPipe de @angular/common.

Tadaaaaam, tes données s'affichent sur ta page Accueil !!!


4. On rend tout ça joli joli

On est content, on a bien nos données du backend qui s'affichent, mais ce n'est pas très beau...

Rappel

On n'oublie pas : dans Angular, tout est composant !
C'est une bonne pratique, lorsque l'on voit le même objet plusieurs fois, de créer un composant réutilisable de cet objet qui a une seule responsabilité.

On va donc créer un composant qui a la responsabilité d'afficher un film !

Normalement, là, ça devrait faire tilt dans ta tête, un nouveau composant = ng generate component + un petit nom

C'est parti !

ng g c home/movie

Maintenant, on remplace nos balises <p></p> par notre nouveau composant dans le home.component.html :

@for (movie of movies$ | async; track movie.id) {
    <app-movie></app-movie>
}
Nous avons besoin de passer les informations de la variable movie à notre MovieComponent.

Pour ça, souviens-toi, c'est avec le décorateur @Input() et le property binding []

Solution
  • Dans movie.component.ts, on ajoute donc @Input({required: true}) movie!: Movie
  • Dans home.component.html, on ajoute le property binding [movie]=movie

Jette un oeil, à ton localhost maintenant que ca recompile ! On itère bien sur le MovieComponent.

On a donc accès à la variable movie dans le movie.component.html et on peut faire un truc tout beau !

On te propose d'utiliser les cards de Bootstrap. Tu peux t'inspirer de ce bout de code :

<div class="card shadow m-4" style="width: 18rem; min-height: 55rem">
  @if(movie.image) {
    <img src="http://localhost:8080/movies/'+movie.id +'/image" class="card-img-top" alt="">
  }
  <div class="card-body">
    <h5 class="card-title">{{movie.title}}</h5>
    <p class="card-text">{{movie.synopsis}}</p>
    <label class="note">Note: {{movie.rate}}</label>
  </div>
</div>

Info

Le @if nous permet d'afficher l'image uniquement si elle existe bien.
On la récupère ensuite depuis un appel HTTP au backend.

Pour que ce soit tout nickel, tu peux ajouter ce style dans le home.component.scss :

:host {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

Et voilaaaaa ton composant home est terminé !

🙋‍♀️ Checkpoint time 🙋‍

Appelle-nous pour qu'on valide cette partie !!️