Les composants
Introduction
Les composants sont au cœur du développement d’applications avec Angular. Ils constituent les blocs fondamentaux de construction des interfaces utilisateur et définissent la façon dont les données sont présentées et manipulées dans l’application. Comprendre les composants et leur fonctionnement est essentiel pour tout développeur Angular.
Un composant est essentiellement composé de trois éléments : un modèle (template) HTML, une classe TypeScript et du code CSS dédié. Ces éléments travaillent ensemble pour créer une unité autonome et réutilisable. Le découpage de composants réutilisables est une pratique clé pour garantir la modularité, la maintenabilité et la testabilité du code. Chaque composant peut être développé, testé et déployé indépendamment des autres, ce qui facilite la collaboration au sein d’une équipe et la gestion des projets complexes.
L’approche par composants encourage également une architecture plus claire et plus structurée. Au lieu de créer une seule page monolithique avec une logique complexe, vous pouvez décomposer les fonctionnalités en morceaux plus petits et plus gérables. Par exemple, une page d’accueil pourrait être composée...
Déclaration d’un composant
1. Création
Les composants Angular sont définis par une classe précédée par un décorateur spécifique @Component, qui fournit des métadonnées permettant à Angular de comprendre comment créer et utiliser ce composant. Dans cette sous-section, nous allons examiner les propriétés clés d’un composant Angular et comment elles sont utilisées pour configurer le comportement et l’apparence du composant.
Voyons un exemple simple de création de composants dits "inline", c’est-à-dire que nous allons définir le style, le modèle HTML ainsi que le code TypeScript dans le même fichier.
Pour créer le composant manuellement, commencez par créer un fichier dans le dossier src/app nommé : "exemple.component.ts", puis, à l’intérieur, créez une classe TypeScript précédée d’un décorateur @Component(). Enfin, en paramètre, il va falloir donner un objet avec les propriétés suivantes :
-
selector
-
template
-
style
D’abord, le selector représente le nom de la balise HTML de notre composant. Le standard de développement veut que nous préfixions notre élément par un mot-clé qui représente soit l’application, soit une section explicite. Par défaut, celui-ci est "app" ; nous allons garder cette valeur pour le moment mais plus de détails à ce propos seront donnés dans la sous-section Standards de ce chapitre. Ensuite, vous pouvez directement ajouter un modèle HTML dans la propriété template, en tant que chaîne de caractères, et faire de même pour la propriété style qui attend un ensemble de règles CSS.
Un autre paramètre important est standalone. Le mode de fonctionnement standalone est opposé à l’utilisation des NgModules ; tout le long du livre, nous utiliserons des éléments dits standalone. Depuis la version 19 d’Angular...
Interactions avec le template
À partir d’ici, nous allons voir les différentes syntaxes pour afficher de la donnée dans le template et comment gérer les événements utilisateur. Il faut savoir que le template d’un composant est uniquement capable d’intégrer les propriétés publiques de votre classe, aucun champ privé ou même aucune variable hors de la classe n’est accessible. Dans l’exemple suivant, seule la propriété secondProperty sera accessible directement.
import { Component } from '@angular/core';
const title:string = "title";
@Component({
selector: 'appExemple',
imports: [],
templateUrl: './exemple.component.html',
styleUrl: './exemple.component.css'
})
export class ExempleComponent {
private firstProperty:string = "first";
secondProperty:string = "second"
}
Maintenant, nous allons voir les différentes façons d’envoyer des valeurs dans notre modèle HTML et, à l’inverse, d’en recevoir vers votre classe TypeScript.
1. Interpolation
La notion d’interpolation est une façon d’injecter du code qui doit être évalué afin de retourner une valeur textuelle pour être affichée dans le modèle HTML. La syntaxe est simple ; il suffit d’utiliser les doubles accolades "{{}}", comme nous l’avions aperçu dans le chapitre Les premières étapes avec Angular, pour lier une donnée à l’interface.
Vous pouvez retourner dans le composant créé précédemment, à l’intérieur de la classe TypeScript, y ajouter une propriété name de type string et lui assigner une valeur avec le texte que vous souhaitez. Puis, une fois dans votre modèle HTML, à la suite de "hello", ajoutez votre propriété avec interpolation.
Voici le code final :
// exemple.component.html
<p>Hello {{name}}</p>
// exemple.component.css
p{
color:red
} ...
Interactions entre composants
Nous avons vu comment un composant pouvait interagir avec son template. Si l’on considère que tout élément HTML peut être un composant, cela veut dire que nous avons déjà entrevu comment un composant dit "parent" partage de la donnée avec un composant "enfant" via le binding. Mais il reste à savoir comment le mécanisme d’un composant dit "enfant" fonctionne, pour se brancher à la syntaxe de binding. C’est ce que nous allons voir maintenant avec les entrées et sorties : Inputs et Outputs.
1. Input
De la même façon que la balise img possède un attribut src qui permet de recevoir une chaîne de caractères, nous allons apprendre à créer nos propres attributs afin de pouvoir récupérer des données de l’extérieur vers notre composant. Pour cela, Angular propose un décorateur spécifique aux données entrantes : @Input.
Nous pouvons reprendre notre composant d’exemple et y ajouter un Input. Pour cela, il faut ajouter une propriété num, de type number, dans la classe TypeScript, simplement précédée du décorateur, puis modifier le modèle HTML pour afficher cette propriété par interpolation :
import { Component } from '@angular/core';
@Component({
selector: 'app-exemple',
template: `<div>
<span>{{num}}</span>
<button (click)="onClick()">Click</button>
</div>`,
styles: [],
})
export class ExempleComponent {
@Input() num:number=0;
onClick(){
console.log('click !');
}
}
Cela signifie que, désormais, lors de l’utilisation de votre composant en HTML, vous pouvez lui assigner un attribut num, avec une valeur numérique, par exemple 10, comme suit :
<!-app.component.html -->
<main class="main">
<app-exemple [num]="10" />
</main>
Maintenant, lors de l’affichage, vous devriez vous retrouver...
Cycle de vie d’un composant
Les composants Angular traversent plusieurs étapes au cours de leur cycle de vie. Ce cycle de vie commence dès que le constructeur de la classe du composant est appelé, déclenchant ainsi une série d’étapes successives, c’est ce que l’on appelle la phase de création. Puis, le mécanisme de détection de changements (change detection) prend les commandes et démarre la phase du même nom (détection de changements), où Angular vérifie et applique les modifications apportées aux données du composant. Plus précisément, au démarrage de cette phase, Angular va initialiser le composant puis attendre tout changement qui pourrait se produire afin de déclencher un nouveau rendu visuel (render).
Enfin, lorsque le composant n’a plus lieu d’exister sur la page courante, on passe en phase de destruction, où les ressources sont nettoyées et libérées avant que le composant ne soit complètement supprimé.
Pour chacune de ces phases, Angular propose une ou plusieurs fonctions qui sont des points d’accroche (ou hooks), que vous pouvez implémenter pour intervenir à des moments précis du cycle de vie.
1. Phase de création
Voici la chronologie complète des hooks pour la phase de création :

Il faut savoir que pour pouvoir implémenter un hook, il vous faudra explicitement définir votre classe comme implémentant l’interface qui correspond. Par exemple, pour le premier hook ngOnChanges, il vous faudra changer la signature de votre classe en ajoutant le mot-clé "implements" (n’hésitez pas à revoir le chapitre TypeScript, sous-section Déclaration de classes pour un rappel sur l’héritage) suivi de l’interface OnChanges. Notez que les fonctions de hooks doivent avoir le même nom que les interfaces avec un préfixe "ng". Voyons les principaux hooks à travers les trois phases du cycle de vie d’une application.
a. ngOnChanges
Cette fonction est appelée juste avant le ngOnInit, puis à chaque fois qu’une des propriétés d’entrée (Input) change. Pour y accéder, il faut d’abord votre classe implémentée...
La projection de contenu
La projection de contenu, ou Content Projection, est une fonctionnalité importante qui permet d’insérer du contenu personnalisé dans un composant depuis l’extérieur, ou plus communément dit, l’imbrication de composants. Elle est souvent utilisée pour créer des composants réutilisables et flexibles, lorsque le contenu exact à afficher est dynamique.
1. Le principe
La projection de contenu est réalisée à l’aide de la balise spéciale <ng-content>. Cette balise agit comme un point d’ancrage dans le modèle HTML d’un composant où le contenu fourni par le parent sera inséré. Prenons l’exemple d’un composant générique qui sert de cadre de design et auquel on veut pouvoir associer n’importe quel composant :
@Component({
selector: 'app-card',
template: `
<div class="card">
<ng-content></ng-content>
</div>
`,
styles: ['.card { border: 1px solid #ccc; padding: 10px; }'],
})
export class CardComponent {}
On va pouvoir l’utiliser tel que :
<app-card>
<h2>Title</h2>
<p>Text content</p>
</app-card>
<app-card>
<app-exemple/>
</app-card>
Dans cet exemple, le contenu entre les balises ouvrante et fermante <app-card> sera projeté à l’intérieur du composant. Cela permet au composant CardComponent d’être réutilisé avec différents contenus tout en conservant une structure et un style cohérent.
2. Multiples projections
Il est possible d’avoir plusieurs projections de contenu dans le même composant, et pour cela nous pouvons ajouter des identifiants à ceux-ci via le duo d’attributs select et ngProjectAs, afin de garantir l’emplacement de chaque projection :
// generic component
<div>
<ng-content select="card-title" />
<hr/>
<ng-content select="card-content" /> ...
Transformer une donnée grâce aux pipes
1. Introduction
Enfin, voyons un dernier élément fortement lié aux composants : le pipe. Un pipe est une fonction de transformation de donnée qui s’utilise principalement directement depuis le template d’un composant. C’est particulièrement utile pour formater une donnée, par exemple si l’on veut afficher une date avec un format particulier ou un montant numérique en suivant les normes de séparation des décimaux ou des milliers.
La syntaxe d’utilisation est simple, il faut qu’à l’intérieur d’un binding notre donnée soit suivie du caractère de trait vertical (ou pipe, d’où son nom) puis le nom de notre pipe, puis, si besoin, de paramètres, et les faire précéder du symbole deux-points ":".
@Component({
selector: 'app-root',
template: `
<main class="main">
{{ myDate | date }}
</main>
`,
})
export class AppComponent {
myDate = new Date();
}
Angular fournit plusieurs pipes intégrés pour des tâches courantes comme le formatage des dates, des devises, ou la transformation de textes. En plus de ces pipes prédéfinis, il nous est possible de créer nos propres pipes personnalisés pour répondre à des besoins spécifiques.
2. Exemples de pipe intégré
Attention, plusieurs formats de données dépendent directement de la localisation, qui est une option configurable dans Angular. Par défaut, les standards et formats américains seront sélectionnés, mais si vous voulez modifier cela, vous le pouvez en spécifiant la langue souhaitée. Par exemple, pour sélectionner les standards français, il faudra suivre les étapes suivantes :
-
importer la configuration française ;
-
l’enregistrer...
Les modules
1. L’origine des modules
Depuis ses premières versions, Angular a utilisé les modules en tant que base solide de l’organisation du code. Un module Angular NgModule était initialement conçu comme un mécanisme de conteneur puissant permettant de regrouper des composants, des services, des directives et autres éléments de manière cohérente et structurée.
Dans l’architecture originelle d’Angular, chaque application devait posséder au moins un module racine, généralement nommé AppModule. Il servait de point d’entrée et de configuration principale pour l’ensemble de l’application. Les développeurs découpaient ensuite leurs projets en modules fonctionnels, chacun représentant un domaine spécifique ou une fonctionnalité distincte.

Voilà un exemple concret de module que l’on trouve dans les applications Angular :
@NgModule({
declarations: [
UserListComponent,
UserDetailComponent
],
imports: [
SharedModule
],
providers: [
UserService
],
exports: [
UserListComponent
]
})
export class UserModule { }
Un module se définit donc avec le décorateur...
Projet fil rouge
Si ce n’est déjà fait, créez un nouveau projet via la commande suivante, puis ouvrez le projet dans votre éditeur préféré :
ng new kanban
Nous sommes à la première étape de progression du projet de tableau Kanban. Nous allons poser les premières briques du projet, centrées sur l’interface et les premiers composants principaux du tableau, c’est-à-dire :
-
un en-tête HeaderComponent composé de :
-
liens de navigation entre les pages dans un menu NavMenuComponent,
-
fonctions de connexion et déconnexion dans un élément UserCardComponent.
-
la page principale BoardPage, composée de :
-
un élément représentant une colonne Column,
-
un second composant Card qui représente un ticket.
S’ajouteront à cela de simples transmissions de données via binding pour l’interaction entre composants et, enfin, un premier pipe personnalisé.
Ici, les manipulations sont assez directes ; nous irons à l’essentiel car cette étape ne comporte pas de spécificité particulière. De même pour le style CSS, vous pouvez ajouter votre propre design ou bien directement récupérer le code depuis les sources sur GitHub. À noter aussi que pour le moment, nous allons définir la donnée en dur dans le code ; la logique et le dynamisme s’ajouteront au fur et à mesure.
1. Ajout de l’en-tête
L’en-tête est constitué des trois composants que nous allons créer dans un dossier app/layout/components/ pour former un ensemble tel que suit :

NavMenuComponent
-
La logique aura un @Input nommé isAdmin qui servira à déterminer si on doit griser ou non le lien de la page d’administration.
-
Le template, quant à lui, doit comporter des balises d’ancre <a> : une pour la page "board" qui est la principale, et une seconde pour une future page d’administration, sans spécifier de lien pour le moment.
UserCardComponent
-
La logique doit pouvoir recevoir une donnée username via @Input et émettre un événement (vide, donc de type void) grâce à un @Output toggleAuth.
-
Le template affichera donc le nom de l’utilisateur...
Conclusion
Dans ce chapitre, nous avons découvert les fondamentaux d’Angular à travers la création et l’interaction de composants. Nous avons exploré les concepts clés comme la déclaration, la gestion des dépendances, et le cycle de vie d’un composant, tout en nous familiarisant avec les notions d’interpolation, de binding, et de communication entre composants grâce aux propriétés @Input et @Output. Nous avons également abordé les pipes, un outil essentiel pour transformer et afficher les données dans un format adapté au sein des templates.
Dans la section pratique, nous avons posé les bases essentielles de notre application Kanban en Angular en créant les composants principaux : la page principale, l’élément de colonne et celui de ticket, permettant de diviser les responsabilités de manière claire. Cet exercice nous a aussi permis d’expérimenter avec les mécanismes de transmission de données, en utilisant Input pour injecter les informations d’un ticket dans le composant Card, et Output pour notifier la page lors de l’ajout d’un ticket dans une colonne. Enfin, nous avons évoqué l’ajout d’un pipe personnalisé pour limiter la longueur des titres des tickets.
Le code final de cet exercice est disponible sur GitHub à l’URL...