Convertir des éléments HTML en composants angulaires

Publié le 25 juin 2021 par Mycamer

De nombreux langages de programmation modernes, tels que Java et Python, sont orientés objet (OO). D’autres sont événementiels. Angular, d’autre part, étant un Framework, suit son propre modèle, utilisant une architecture basée sur des composants. Nous entendons par là qu’une application Angular est composée de nombreux blocs réutilisables appelés Composants. Chacun est composé de plusieurs fichiers, qui incluent un fichier de classe .ts, un modèle .html et peut-être un fichier de test .css et spec.ts. L’idée derrière l’architecture basée sur les composants d’Angular est solide ; en construisant des pièces réutilisables, nos applications peuvent réduire considérablement le nombre de doublons.

Dans de nombreux cas, nous pouvons planifier les pièces à transformer en composants. D’autres fois, nous réalisons que nous aurons besoin de fonctionnalités similaires à quelques endroits de notre application. Et c’est le sujet du tutoriel : comment extraire le TypeScript, HTML et CSS que nous devons répliquer et comment tout compartimenter dans un nouveau composant. En raison des règles de portée imposées par Angular, quoi laisser et quoi retirer peut être une question difficile à répondre. Comme étude de cas, nous prendrons le menu déroulant personnalisé que nous avons implémenté dans le Basculement de la visibilité des éléments dans Angular 11 avec NgClass article et en faire un composant afin que nous puissions le réutiliser plus tard.

Échanger le menu déroulant existant avec notre nouveau composant

Il est généralement préférable d’éviter de tout faire en même temps ; cela produira probablement beaucoup de bogues qui seront difficiles à traquer. Au lieu de cela, abordez la tâche en utilisant des petits pas en créant votre nouveau composant et en remplaçant le ou les éléments existants par celui-ci.

En utilisant la CLI angulaire, la commande serait :

ng g c shared/components/dropdown-menu

Le “cg” est l’abréviation de “Generate Component”, donc, tant que vous savez ce que vous tapez, c’est bien d’utiliser la forme courte. La commande ci-dessus placera notre nouveau composant de menu déroulant sous « srcappsharedcomponentsmenu déroulant”:

Une fois notre composant en place, nous sommes prêts à l’insérer dans notre modèle. Pour ce faire, nous allons simplement commenter le balisage en question et ajouter le composant de menu déroulant :

<mat-toolbar color="primary">

  <a [routerLink]="['/']">Home</a>  

  <a [routerLink]="['/survey']">Survey</a>  

  <div class="menu-container" 

    (click)="menuOpened = true" (mouseenter)="menuOpened = true" (mouseleave)="menuOpened = false">

    <span>Menu</span>

    <dropdown-menu [ngClass]="{ 'menu-opened': menuOpened }" class="dropdown-menu"></dropdown-menu>

    <!--

    <div [ngClass]="{ 'menu-opened': menuOpened }" class="dropdown-menu">

        <a *ngFor="let ic of investmentClasses" (click)="onClick($event)">

            <mat-icon mat-list-icon>{{ ic.icon }}</mat-icon>

            <span class="dropdown-menu-item">{{ ic.text }}</span>

        </a>

    </div> 

    -->

  </div>

</mat-toolbar>

  

<div class="container">

  <router-outlet></router-outlet>

</div>

Pour styliser notre nouveau composant comme le ou les éléments que nous remplaçons, nous pouvons copier la classe “menu déroulant”. Par défaut, le menu déroulant affiche « aucun », nous devons donc également nous déplacer sur le ngClass. Cela changera la propriété d’affichage en « flex » sur l’événement de survol du DIV du conteneur de menu :

Communiquer avec notre nouveau composant

Le plus grand défi lors de l’introduction d’un nouveau composant dans un modèle est la transmission de données entre le composant parent et enfant. Dans notre cas, les données doivent voyager dans les deux sens : le menu déroulant doit savoir quels éléments afficher et le parent doit être informé de la sélection de l’utilisateur.

Commençons par définir les éléments du menu déroulant.

Dans Angular, le moyen de transmettre des données directement à un composant consiste à utiliser un décorateur @Input(). En ajoutant [menuItems]="investmentClasses" à la balise de menu déroulant, la variable d’entrée menuItems de notre composant sera définie sur les classes d’investissement de l’AppComponent.

Dans le DropdownMenuComponent, nous devons maintenant ajouter la variable menuItems :

export class DropdownMenuComponent {

  @Input() public menuItems: Array<MenuItem> = [];

  //...

}

Nous voulons que les menuItems aient un format très spécifique afin que les propriétés ne manquent pas dans le modèle. Pour ce faire, nous pouvons définir l’interface MenuItem :

export interface MenuItem {

  icon: string;

  id: string;

  value: string,

}

L’interface MenuItem est exportée à partir de DropdownMenuComponent afin qu’elle puisse être utilisée par AppComponent (bien que cela ne soit pas strictement nécessaire, tant que les propriétés de l’élément correspondent) :

import { MenuItem } from "./shared/components/dropdown-menu/dropdown-menu.component";



export class AppComponent {

  public menuOpened = false;

  public investmentClasses: MenuItem[] = [

     {

       icon: "euro_symbol",

       value: "currencies",

       id: "currency"

     },

     //...

  ];

}

Notifier le parent de la sélection de l’utilisateur

Tout comme le décorateur @Input() accepte les données du composant parent, l’analogue @Output() émet des données vers le parent. Pour cette raison, il doit être instancié dans une nouvelle instance EventEmitter :

export class DropdownMenuComponent {

  @Input()  public menuItems: Array<MenuItem> = [];

  @Output() public itemSelected = new EventEmitter<number>();

  //...

}

Ce que vous choisissez d’émettre au parent dépend du format de données. Puisque nous utilisons un tableau, le moyen le plus simple d’accéder à un élément est via son index numérique. Un moyen simple de le faire est de l’ajouter à la boucle ngFor. Cela nous permet de le transmettre au gestionnaire d’événements onClick :

<a *ngFor="let mi of menuItems;let i = index" (click)="onClick($event, i)">

Nous pouvons maintenant émettre l’index vers le parent :

public onClick(event: MouseEvent, index: number) {

  event.stopPropagation();

  this.itemSelected.emit(index);

}

Pendant ce temps, dans le modèle du parent, nous pouvons ajouter un gestionnaire pour l’élément sélectionné comme suit :

(itemSelected)="onItemSelected($event)"

Voici le code de la méthode onItemSelected() qui montre comment récupérer l’élément investmentClasses pertinent :

public onItemSelected(index: number) {

  this.menuOpened = false;

  alert('Selected item: ' + this.investmentClasses[index].value);

}

Styliser le composant du menu déroulant

Pour maintenir une encapsulation de composant appropriée, toute classe définie dans le modèle du menu déroulant doit être déplacée dans son fichier .css :

a {

  text-decoration: none;

  display: inline-flex;

  cursor: pointer;

  align-items: center;

  padding: 0 0.8rem;

}



a:not(:last-child) {

  border-bottom: 2px solid gray;

}



a:hover {

  background-color: #a12f42;

}



.dropdown-menu-item {

  display: block;

  margin: 1.33rem 0rem;

  font-weight: bold;

}

La démo

Au codesandbox.io, vous trouverez la démo avec tout le code qui a été présenté dans cet article.

Conclusion

Chaque fois que vous avez besoin de réutiliser des éléments HTML dans différentes pages, ou même dans la même page, c’est le moment d’encapsuler ses fonctionnalités dans un composant personnalisé. Comme nous l’avons vu ici aujourd’hui, ce n’est pas si difficile; il suffit d’adopter une approche au coup par coup.

— to www.htmlgoodies.com