Les Directives Angular permettent d'ajouter du comportement à des éléments HTML. En réalité, tous les components sont des Directives qui ont la spécificité d'avoir aussi un template associé !
Vous utilisez souvent les Directives fournies par Angular :
les structurelles, comme
*ngIf
ou*ngFor
;les attributs, comme
[ngClass]
,[ngStyle]
, ou même[formGroup]
etformControlName
.
Toutes ces Directives viennent ajouter des comportements spécifiques aux éléments sur lesquels elles sont placées. C'est comme si on créait des mini-components autour de ces éléments, l'avantage étant que la même Directive peut être placée sur plusieurs types d'éléments, et à plusieurs endroits de l'application.
Dès que vous avez un comportement que vous souhaitez pouvoir ajouter facilement à plusieurs éléments, la Directive s'annonce généralement comme étant la meilleure approche.
Dans ce chapitre, vous allez créer une Directive simple qui surligne le texte sur lequel elle est placée. Vous y apporterez ensuite quelques améliorations pour qu'elle réagisse aux passages de la souris.
Soyez Directives
La Directive pourra être utilisée dans toute l'application, donc elle a tout intérêt à être dans SharedModule. Dans le dossier shared
, créez un dossier directives
et un fichier highlight.directive.ts
:
import { AfterViewInit, Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[highlight]'
})
export class HighlightDirective implements AfterViewInit {
constructor(private el: ElementRef,
private renderer: Renderer2) {}
ngAfterViewInit() {
this.setBackgroundColor('yellow');
}
setBackgroundColor(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', color);
}
}
Il y a plusieurs nouvelles choses ici :
d'abord, le décorateur
@Directive
, à qui on passe un objet de configuration avec unselector
, un peu comme pour les components. Pour pouvoir placer cette directive en tant qu'attribut HTML, leselector
doit être entre crochets[]
;la Directive implémente le lifecycle hook
AfterViewInit
, car vous devez vous assurer que la view existe avant de commencer à manipuler des éléments qui s'y trouvent ;dans le
constructor
, vous injectezElementRef
etRenderer2
:ElementRef
est la référence à l'élément du DOM, et l'injection d'Angular vous permet de l'injecter directement comme ça,Renderer2
est un outil qui vous permet d'interagir avec le DOM de manière testable, c'est-à-dire que vous pourrez écrire des tests unitaires – qui peuvent être exécutés dans un contexte où le DOM n'existe pas – qui fonctionneront correctement ;
après l'initialisation de la view, vous utilisez la méthode
setStyle
deRenderer2
pour changer la couleur de fond de l'élément sur lequel la Directive se trouve – l'objetElementRef
le met à disposition via son objetnativeElement
.
Il suffit maintenant de déclarer et d'exporter la Directive dans SharedModule, et vous pourrez ensuite l'appliquer sur les éléments de votre application. J'ai choisi de l'appliquer sur les titres des Posts.
Dans post-list-item.component.html
:
<span highlight>{{ post.title | titlecase }}</span>
C'est aussi simple que ça d'attribuer une Directive ! Dans l'application, ça donne :
Vous pouvez bien sûr appliquer cette Directive à n'importe quel élément HTML, et transformer sa couleur de fond en jaune !
Mais comment pourrait-on aller plus loin ?
Choisissez votre couleur
Pour permettre aux développeurs qui utilisent votre Directive de choisir la couleur qui est utilisée, vous pouvez y ajouter un @Input
, comme pour un component :
import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[highlight]'
})
export class HighlightDirective implements AfterViewInit {
@Input() color = 'yellow';
constructor(private el: ElementRef,
private renderer: Renderer2) {}
ngAfterViewInit() {
this.setBackgroundColor(this.color);
}
setBackgroundColor(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', color);
}
}
Vous attribuez une valeur par défaut pour pouvoir utiliser highlight
tout seul, mais ici vous permettez également ce genre de syntaxe :
<span highlight color="orange">{{ post.title | titlecase }}</span>
Ce qui donne :
Sachez qu'il est également possible de donner le même nom à votre @Input
que le selector
de la Directive ; par exemple :
@Input() highlight!: string;
Ce qui donne, côté template, une syntaxe très lisible :
<span highlight="lightblue">{{ post.title | titlecase }}</span>
Je pense que dans le cas présent, le meilleur choix est de laisser color
comme @Input
, car ça laisse l'option de ne pas passer de couleur en paramètre si on souhaite garder la couleur par défaut. Je trouve que ça laisse le plus de flexibilité.
Voyons maintenant comment réagir à des événements dans la Directive !
Écoutez l'hôte
Il peut être très intéressant, pour une Directive, de pouvoir réagir aux événements HTML émanant de l'élément sur lequel elle est placée. Angular vous permet de faire exactement ça avec le décorateur @HostListener
:
import { AfterViewInit, Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[highlight]'
})
export class HighlightDirective implements AfterViewInit {
@Input() color = 'yellow';
constructor(private el: ElementRef,
private renderer: Renderer2) {}
ngAfterViewInit() {
this.setBackgroundColor(this.color);
}
setBackgroundColor(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', color);
}
@HostListener('mouseenter') onMouseEnter() {
this.setBackgroundColor('lightgreen');
}
@HostListener('mouseleave') onMouseLeave() {
this.setBackgroundColor(this.color);
}
@HostListener('click') onClick() {
this.color = 'lightgreen';
}
}
Dans cet exemple :
quand la souris entre sur l'élément, vous changez sa couleur de fond en
lightgreen
;si la souris sort sans avoir cliqué, la couleur revient à sa couleur par défaut ;
si l'utilisateur clique sur l'élément, la couleur par défaut est changée en
lightgreen
, ce qui donne l'effet de "valider" ce qui est cliqué : l'élément resteralightgreen
en permanence.
La Directive est une arme essentielle dans l'arsenal des développeurs Angular, donc pensez-y lorsque vous aurez besoin d'apporter du comportement à des éléments HTML de votre application.
En résumé
Une Directive peut être placée sur différents types d'élément HTML pour leur apporter un comportement supplémentaire.
Le selector d'une Directive attribut doit s'écrire entre crochets
[]
.Une Directive peut injecter l'élément HTML sur lequel elle est placée avec
ElementRef
, et interagir avec cet élément avecRenderer2
.Une Directive peut comporter des
@Input
pour accepter des paramètres.Une Directive peut écouter les événements émanant de son élément grâce au décorateur
@HostListener
.
Qu'avez-vous appris dans cette partie du cours ?
Vous avez implémenté un resolver pour récupérer des données entre deux routes.
Vous avez construit un component totalement réutilisable.
Vous avez créé vos propres Pipes.
Vous avez développé votre propre Directive.
Je vous ai concocté un quiz pour valider ce que vous avez appris de cette partie. Après, je vous donne rendez-vous dans la partie 2 pour découvrir les animations !