Episode 9 : Dans l’épisode précédent, nous avons vu théoriquement comment organiser les nombreux composants qui constitueront notre application, grâce à l’architecture en composant Smart & Dumb. Maintenant, il nous reste « juste » à passer à la pratique, en mettant en application ces beaux principes théoriques. Par quoi commencer ? Quel composant créer en premier ? Comment les imbriquer entre eux ?

Nous savons théoriquement à quoi ressemble une architecture en composants « intelligents » et « stupides », mais nous ne l’avons jamais encore mis en place pour de vrai. Nous allons commencer par découper notre page d’accueil en différents composants, puis nous les générerons avec Angular CLI. Enfin, nous les assemblerons tous ensemble afin d’afficher une magnifique page d’accueil à nos futurs utilisateurs.

Générer les composants de la page d’accueil

Pour créer notre page d’accueil, nous avons vu que nous allons avoir besoin de plusieurs composants différents. Avant de commencer à coder, je vous propose de découper notre page en prenant en compte l’arborescence des composants et le principe de responsabilité unique que nous avons vu précédemment :

Le découpage en composants de notre page d’accueil !

La capture d’écran à gauche représente ce que l’utilisateur doit voir, et droite, ce qu’un développeur web moderne doit voir, c’est-à-dire une arborescence de composants :

  • Home : On commence par créer le composant de la page d’accueil, et on remplace la page par défaut affiché par Angular CLI. Bon débarras!
  • HomeBanner : On développe un composant dumb, qui s’occupe uniquement d’afficher la bannière, et on l’intègre dans notre page d’accueil.
  • HomeFeatureCard : On développe un autre composant dumb pour afficher une carte de fonctionnalité.
  • HomeFeatures : On ajoute un composant smart, qui a pour rôle de fournir les données des fonctionnalités à afficher : combien de fonctionnalités, quels titres, quelles icônes, etc. On l’intègre ensuite avec notre composant dumb qui affiche les cartes de fonctionnalité, et enfin on termine d’assembler notre page d’accueil !

Vous remarquerez qu’on génère d’abord notre composant de page, et qu’on ajoute au fur et à mesure les composants qui constituent cette page, en emboitant les composants les uns avec les autres, jusqu’à former un ensemble cohérent. C’est le même fonctionnement que pour les poupées russes.

Pour commencer, il faut générer les composants suivants grâce à Angular CLI :

// Composant de page Home
ng generate component public/home/home --module home --inline-style --spec false

// Composant dumb de la bannière
ng generate component public/home/home-banner --module home --spec false

// Composant dumb de l’affichage d’une carte de fonctionnalité
ng generate component public/home/home-feature-card --module home --inline-style --spec false

// Composant smart des fonctionnalités
ng generate component public/home/home-features --module home --inline-style --spec false

On passe plusieurs options à Angular CLI. On précise le module auquel appartiennent les composants, à savoir le module Home. Mais ça, vous le savez déjà. L’option inline-syle permet d’indiquer à Angular CLI de ne pas générer une feuille de style dans un fichier à part pour votre composant, et spec avec le paramètre false indique de ne pas générer une classe de test pour votre composant.

Les tests feront l’objet d’un chapitre dédié plus loin dans ce cours. Pour le moment, retenez que nous n’ajoutons pas des tests dans notre application.

Une fois ces commandes exécutées, vous devriez obtenir l’arborescence suivante dans votre éditeur de code :

L’arborescence des composants que nous avons générés.

Vous remarquerez que nous avons généré un fichier de style home-banner.component.scss pour le composant de la bannière, car nous n’avons pas précisé l’option inline-style à Angular CLI lors de la génération de ce composant. Cela nous permettra d’ajouter l’image de fond pour la bannière.

Maintenant, nous allons remplacer l’affichage par défaut de Angular CLI par notre propre page d’accueil. Ensuite nous assemblerons les éléments les uns après les autres.

Mise en place de la page d’accueil

La première chose à faire, c’est de dire à notre application Angular que par défaut nous souhaitons afficher la page d’accueil, à l’adresse “/home”. Pour cela, ouvrez le fichier principal de configuration de vos routes, à savoir app-routing.module.ts. A l’intérieur de ce fichier, ajoutez la route suivante :

01 const routes: Routes = [
02  { path: '', redirectTo: '/home', pathMatch: 'full' }
03 ];

Nous avons défini une nouvelle route par défaut. Concrètement, nous disons à Angular que lorsque l’application démarre, ou lorsque l’utilisateur souhaite accéder à une route vide, nous le redirigeons vers la route “/home”. C’est ce que nous faisons grâce aux options path et redirectTo.

Et l’option pathMatch ?

On peut passer deux valeurs à cette option : full ou prefix. On précise donc simplement que la redirection se fait uniquement dans le cas de la route vide. Mais nous verrons plus loin en détail comment fonctionne la gestion des routes, dans une application Angular.

Pour le moment, on redirige l’utilisateur vers une route qui n’existe pas encore. Nous devons faire pointer la route “/home” vers notre composant de page Home. Pour cela, rien de plus simple, il suffit d’ajouter une nouvelle route dans la partie publique de notre application. Le fichier à modifier est public-routing.module.ts :

01 const routes: Routes = [
02  { path: 'home', component: HomeComponent }
03 ];

Le code peut se comprendre de lui-même, on fait correspondre la route “/home” à un composant Home. C’est aussi simple que ça !

Par contre, notre application possède maintenant une page unique, qui est la page d’accueil. Or, nous avons encore dans notre projet du code inutile qui servait à afficher la page d’accueil par défaut d’Angular CLI. Nous devons donc faire un peu de nettoyage. Cela nous permettra de repartir sur une base propre. Ouvrez donc le template du composant racine app.component.html, et supprimer tout le code sauf la balise ci-dessous :

01 <router-outlet></router-outlet>

Il s‘agit d’une balise spécifique à la gestion des routes dans Angular, mais nous reviendrons dessus plus tard. Retenez simplement que lorsque nous naviguons dans notre application, le template des composants de page est injecté dans cette balise. Nous devons donc la conserver au niveau de notre template racine.

Dans la classe du composant racine app.component.ts, supprimez également la propriété title,devenue inutile.

Si vous redémarrez votre application, vous devriez désormais voir s’afficher ce message :

  home works!

C’est parfait, nous pouvons passer à la suite, et commencer à construire notre page d’accueil !

Le composant de la bannière

Il faut que nous ajoutions les composants qui constituent la page d’accueil. Nous allons commencer par le composant de la bannière. Il s’agit d’un composant simple, contenant uniquement du code HTML dédié à l’affichage.

La bannière de notre page page d’accueil.

Ouvrez donc le fichier home-banner.component.html généré par Angular CLI, et ajoutez le code suivant :

01 <!-- Bannière -->
02 <div class="jumbotron text-center">  
03  <h1 class="display-4 text-white">
04   La productivité au 21ème siècle
05  </h1>
06  <h2 class="text-white">
07   Atteignez plus d'objectifs en moins de temps.
08  </h2>
09  <p class="lead mt-4">
10   <a class="btn btn-success btn-lg"
11    href="#"
12    role="button"
13    routerLink="/app/dashboard">
14    <strong>Terminez vos journées en héros</strong>
15   </a>  
16  </p>
17 </div>

Il s’agit simplement de code HTML permettant d’afficher une bannière. Nous n’avons même pas à modifier la classe TypeScript  de ce composant, qui a été générée par Angular CLI. La bannière est maintenant définie dans son propre composant. Si on souhaite afficher cette bannière sur deux pages différentes, il suffira d’appeler ce composant autant de fois que nécessaire.

On a également plusieurs classes assignées à différentes balises. Ces classes ont été définis par Bootstrap, et nous nous contentons d’appliquer les règles de style définies par cette librairie. Le nom des classes donne une bonne idée de leur rôle : “text-center” pour centraliser du texte, ou encore “btn” pour définir un bouton. En cas de doute, n’hésitez pas à googliser le nom d’une classe. Je préfère ne pas m’étendre plus sur le sujet, afin de me concentrer sur la partie Angular. J’espère que vous me comprendrez ! Un tableau d’une demi page pour définir les classes une par une risquerait d’endormir tout le monde, et on raterait l’essentiel.

Ensuite, pour intégrer ce composant dans notre page d’accueil, rien de plus simple. Il suffit d’ajouter ce nouvel élément dans le fichier home.component.html. Remplacez le code générer par Angular CLI par celui-ci :

01 <al-home-banner></al-home-banner>

Ensuite, vous pourrez voir votre bannière s’afficher sur la page d’accueil.

Mais… pourquoi la bannière est toute grise ? Où est l’image de fond que j’ai aperçu sur la capture d’écran ?

Vous avez raison, nous devons ajouter un peu de style à notre bannière. Pour cela, nous devons commencer par ajouter cette image de fond au projet (clic droit > « Enregistrer l’image sous… »).

L’image de fond de la bannière que vous devez télécharger.
(Clic droit > « Enregistrer l’image sous… »).

Dans le dossier assets de votre projet, créez un nouveau dossier nommé img, et ajoutez l’image home-banner.jpg. Maintenant, nous devons ajouter le style de notre composant de bannière dans le fichier dédié home-banner.component.sass :

01 .jumbotron {
02  background-image: 
03   url('../../../../assets/img/home-banner.jpg');
04  background-size: cover;
05  min-height: 380px;
06 }

Nous ajoutons simplement quelques règles de style, pour indiquer que nous ajoutons une image de fond à notre bannière, qui prend toute la surface du composant sans déformer l’image, quitte à la rogner, et qui fait au minimum 380px de hauteur.

Ensuite, si vous retournez dans votre navigateur, vous pourrez voir votre magnifique page d’accueil doté d’une bannière. Il s’agit du premier composant de votre application. Félicitations, c’est un bon début !

Les composants de fonctionnalités

Nous allons nous attaquer à l’affichage des fonctionnalités sur la page d’accueil. Il s’agit d’une partie légèrement moins facile, car nous devons factoriser la logique d’affichage d’une carte qui présente une fonctionnalité de l’application. Nous aimerions obtenir ceci :

Notre page d’accueil une fois tous les composants assemblés.

La première chose à faire, c’est donc d’identifier les données qui varient d’un composant de fonctionnalités à un autre. En effet, ce sont ces données qui seront attendues par le composant HomeFeatureCard pour afficher le bloc de la fonctionnalité. On a donc :

  • Un titre. (Planifier sa semaine, par exemple)
  • Une icône. (Une icône de calendrier)
  • Une description. (Visibilité sur les 7 prochains jours)

Maintenant que l’on a identifié les données attendues par notre composant, on peut les déclarer sous forme de propriétés. Ajoutons-les tout de suite, dans le fichier home-feature-card.component.ts :

01 import { Component, Input } from '@angular/core';
02 
03 @Component({
04  selector: 'al-home-feature-card',
05  templateUrl: './home-feature-card.component.html',
06  styles: []
07 })
08 export class HomeFeatureCardComponent {
09  @Input() description: string;
10  @Input() icon: string;
11  @Input() title: string;
12 }

Dans ce composant, j’ai simplement déclaré trois nouvelles propriétés, de la ligne 9 à 11, que nous avons identifiées précédemment. L’annotation @Input, que je vous présenterai un peu plus loin dans ce chapitre, permet de définir les propriétés attendues par le composant, sans qu’il ne doive les récupérer par lui-même. On considère simplement qu’elles seront disponibles, et qu’elles devront être injectées par le composant parent. Avec ces données en entrée, nous pouvons développer le template de ce composant, c’est-à-dire sa logique d’affichage. Remplacez donc le code du fichier home-feature-card.component.html par ce code :

01 <!-- Carte de fonctionnalité -->
02 <div class="card mb-3">
03  <h3 class="card-header">{{ title }}</h3>
04  <div class="card-body">
05   <img [src]="icon" class="w-25 img-fluid mx-auto d-block"/>
06  </div>
07  <ul class="list-group list-group-flush">
08   <li class="list-group-item text-center">
09    {{ description }}
10   </li>
11  </ul>
12 </div>

Comme vous pouvez le voir, il y a dans ce template plusieurs liaisons vers les propriétés que nous avons définies :

  • L’interpolation avec la propriété title, à la ligne 3.
  • La liaison entre la propriété icon et la source de l’image à la ligne 5.
  • L’interpolation avec la propriété description, à la ligne 3.

Et maintenant nous disposons d’un composant dumb, prêt à afficher les fonctionnalités. Enfin presque, puisque ce composant attend en entrée des données, que nous ne sommes pas encore en mesure de lui fournir.

😉

Mais nous n’avons pas dit notre dernier mot. Nous allons utiliser notre composant HomeFeatures, car c’est à lui de transmettre les données aux composants situés plus bas dans l’arborescence. Nous allons donc initialiser une nouvelle propriété features dans la classe du composant home-features.component.ts, qui contiendra ces fameuses données :

01 import { Component, OnInit } from '@angular/core';
02
03 @Component( … )
04 export class HomeFeaturesComponent implements OnInit {
05  features;
06
07  constructor() { }
09  ngOnInit() {
10   this.features = [
11    {
12     title: 'Plannifier sa semaine',
13     description: 'Visibilité sur les 7 prochains jours',
14     icon: 'assets/img/calendar.png'
15    },
16    {
17     title: 'Atteindre ses objectifs',
18     description: 'Priorisation des tâches',
19     icon: 'assets/img/award.png'
20    },
21    {
22     title: 'Analyser sa productivité',
23     description: 'Visualiser le travail accompli',
24     icon: 'assets/img/diagram.png'
25    }
26   ];
27  }
28 }

On initialise une propriété features dans la méthode ngOnInit. Cette propriété est un tableau contenant les données des fonctionnalités que l’on souhaite afficher. Dans notre cas, on souhaite afficher trois fonctionnalités, et pour chaque fonctionnalité nous passons le titre, la description et une icône, comme définis précédemment.

Une fois que nous avons défini les données des fonctionnalités, nous pouvons les passer aux composants dumb en dessous. Pour cela, on passe par le template home-features.component.html :

01 <div class="container">
02  <div class="row">
03    <al-home-feature-card *ngFor="let feature of features"
04      class="col-md-4"
05      [title]="feature.title"
06      [description]="feature.description"
07      [icon]="feature.icon">
08    </al-home-feature-card>
09  </div>
10 </div>

Dans ce template, il y a deux choses essentielles que vous devez remarquer:

  • A la ligne 3, on utilise la directive ngFor que vous connaissez déjà. Mais ce n‘est pas ça le plus important. Ce qui compte, c‘est que l’on effectue une boucle sur notre composant al-home-feature. On affiche ainsi autant de blocs de fonctionnalités que nécessaire.
  • De la ligne 5 à 7, on transmet les données aux composants dumb,grâce à la liaison de données. Concrètement, regarder l’équivalence suivante :

(composant smart)

[title]=”monTitre” 

<=>

 @Input() titre: string;

(composant dumb)

 En haut, on passe la donnée depuis le composant smart, et à droite on la récupère dans le composant dumb grâce à l’annotation @Input. C’est comme ça que l’on fait le lien entre les deux composants pour les propriétés title, description et icon. C’est tout !

Et on a renseigné des chemins pour les icônes, on doit les ajouter aussi non ?

Effectivement, il nous reste à ajouter les icônes à ajouter. Vous pouvez les récupérer ci-dessous (clic droit > « Enregistrer l’image sous… »), et ensuite ajoutez les dans le dossier src/assets/img de votre projet :

A partir de là, notre composant chargé d’afficher les fonctionnalités est prêt. Mais il n’est pas encore visible sur notre magnifique page d’accueil. Pour cela rien de plus simple, il suffit de l’ajouter dans le template home.component.html :

01 <al-home-banner></al-home-banner>
02 <al-home-features></al-home-features>

Et maintenant, la magie devrait opérer !

Si vous retournez dans votre navigateur, vous devriez voir une page d’accueil avec tous les éléments que nous attendions. Parfait !

Cependant, avant de passer à la suite, je souhaiterais mettre deux trois petites choses au clair. Je suis sûr que vous vous dites “Tout ça pour ça ?”. Effectivement, au début cela peut être surprenant. Quand j’ai démarré avec Angular, je n’avais pas envie de créer beaucoup de nouveaux composants. J’aurai préféré avoir un seul composant Home, avec tout le code nécessaire à l’intérieur.

Mais le code que nous avons développé présente plusieurs avantages :

  • Nos composants sont facilement maintenables : Ils font quelques dizaines de lignes seulement. Il est beaucoup plus facile de trouver une éventuelle erreur. Et il est plus facile d’apporter une modification que sur un composant de mille lignes.
  • Chaque composant a un rôle unique : On ne s’interroge pas sans cesse pour savoir où est la partie récupération des données, et où est la logique d’affichage. Chaque élément est à sa place.
  • Le composant de page est simplissime : Si un nouveau développeur lit votre code, il comprendra tout de suite que cette page affiche une bannière et des fonctionnalités, et rien de plus.
  • Notre code est extensible : Si nous voulons ajouter une fonctionnalité sur la page d’accueil, il suffit d’ajouter les données nécessaires dans le composant HomeFeatures. Et hop, l’affichage se met à jour. C’est une des différences entre du bon code et du code “moyen”. Chaque élément que l’on ajoute dans le projet est un brique sur laquelle on peut s’appuyer pour les futurs développements.

Voilà, on a fini pour aujourd’hui. Félicitations ! Enfin on a quelque chose de concret à afficher à nos utilisateurs !

Même si pour une simple page d’accueil, ces avantages ne vous semblent pas forcément intéressants, faites-moi confiance pour la suite. Quand nous développerons des éléments beaucoup plus complexes, nous serons contents de raisonner de cette manière, et de bien découper nos pages. S’entraîner sur cette page d’accueil n’était donc pas une perte de temps !

Dans le prochain épisode, nous verrons comment ajouter une barre de navigation à notre page d’accueil, afin de l’habiller un peu plus. Nous verrons aussi comment faire en sorte que cette barre de navigation s’affiche sur toutes les pages de notre application, et pas seulement sur la page d’accueil. A très vite !


Si vous n’avez pas la patience de chercher les articles sur le blog, je peux vous les envoyer dans l’ordre, directement dans votre boîte mail. En fait, cet article fait partie d’une série de 10 articles extraits de l’ouvrage Maîtriser Angular pour l’entreprise.