I. Les différentes directives▲
I-A. Les directives structurelles▲
Je ne vais pas lister toutes les directives structurelles proposées par Angular, mais nous allons quand même en étudier une pour l'exemple : ngIf. ngIf permet de supprimer ou de recréer l'élément courant suivant l'expression passée en paramètre. Si l'expression assignée au ngIf est évaluée à false alors l'élément sera supprimé du DOM, puis l'élément sera recréé si l'expression est évaluée à true. Voici un exemple de template utilisant cette directive :
<div *ngIf
=
"1 > 0"
>
Afficher la div</div>
Afficher la div sera bien affiché puisque l'expression 1 > 0 vaut bien true. Je vous propose maintenant d'inverser le sens de l'expression de sorte que 1 < 0. Dans ce cas, notre div disparait totalement du DOM.
Vous vous posez sans doute la question du symbole astérisque (*) devant notre directive. Les directives structurelles telles que *ngIf, *ngForet *ngSwitchCase sont toutes encadrées par une balise HTML5 <template>. L'astérisque (*) permet de lire et d'écrire plus facilement des directives qui ont vocation à changer la mise en page HTML. Prenons l'exemple de notre directive ngIf : lorsque Angular va détecter (*) devant *ngIf, il va opérer différentes transformations pour arriver au résultat suivant :
<template [ngIf]=
"1 > 0"
><div> Afficher la div</div></template>
Notre directive ngIf sera donc maintenant un template HTML 5 et pourra interpréter l'expression passée en paramètre grâce au Property Binding (usage des [ ]).
I-B. Les attribute directives▲
Une bonne candidate pour comprendre les attribute directives est la directive ngStyle. Cette attribut directive se charge de modifier l'apparence de l'élément porteur. Lorsque l'on utilise cette directive, il est nécessaire de la placer entre crochets comme ceci : [ngStyle]. Ce n'est pas forcément le cas de toutes les attribute directives. ngStyle attend une valeur en paramètre, elle utilise ce que l'on appelle le Property Binding que nous reverrons plus tard. Voici un exemple de template utilisant cette directive :
<div [ngStyle]=
"{color:'red'}"
>
Learn Angular</div>
Dans ce cas, il n'y a pas de modification de structure. La directive transforme juste notre [ngStyle]="{color:'red'}" en style="color: red;".
II. Construire ses directives▲
Je vous propose maintenant de créer notre propre directive pour bien en comprendre le fonctionnement. Voici notre cahier des charges :
Réaliser une directive qui permet de modifier la couleur lorsque le pointeur de la souris passe dessus.
À la lecture de cette spécification « post-it », une réunion a été organisée avec la maitrise d'ouvrage. Concernant la couleur, aucun compromis n'a été trouvé après 3 heures de débat même si le rouge intéressait pas mal d'intervenants. Pour contenter tout le monde, notre Directive devra proposer une couleur par défaut (le rouge) lors de l'affichage et une autre couleur paramétrable pour le mouse enter et tout cela avec le même budget.
À la lecture du compte rendu de cette réunion, nous pouvons déjà nous dire que nous sommes face à une attribute directive.
II-A. Création de notre Directive▲
Commençons par créer notre directive à l'aide d'Angular-CLI avec la commande :
Cela nous donne le code suivant pour notre directive :
Pourquoi Angular-CLI ajoute des [ … ] à notre sélecteur ? Angular utilise les sélecteurs CSS pour identifier l'élément HTML dans un template. Grâce à cette annotation, Angular sera capable de localiser tous les éléments qui possèdent l'attribut nommé appHighLight.
La première étape consiste à appliquer la couleur par défaut lorsque l'on applique cette directive. Comme nous sommes dans une classe, nous pouvons utiliser le constructeur comme ceci :
ElementRef est injecté dans le constructeur par Angular afin de récupérer l'élément du DOM dans notre classe. Grâce à cette injection, il est possible de modifier le style de l'élément grâce à Renderer2. Vous pouvez tester votre directive dans le template d'un de vos Components comme ceci :
<div appHighlight>Texte en hightlight</div>
Bon, on ne va pas se mentir, le résultat est plutôt décevant. Le texte est rouge certes, mais cela s'arrête là. On va dynamiser tout cela alors…
II-B. Ajouter du component grâce aux events JavaScript▲
Notre directive doit réagir à deux évènements :
- Lorsque le pointeur de la souris est sur l'élément : la couleur du texte doit passer à une couleur paramétrable.
- Lorsque le pointeur de la souris quitte l'élément : la couleur du texte doit redevenir rouge.
Angular propose le décorateur @HostListener pour positionner des Listeners sur l'élément. Il prend en paramètre l'évènement que l'on veut écouter. Notre classe devient alors :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
import {
Directive,
Renderer2,
ElementRef,
HostListener}
from '@angular/core'
;
@Directive
({
selector
:
'[appHighlight]'
}
)
export class HighlightDirective {
constructor
(
el
:
ElementRef,
renderer
:
Renderer2) {
renderer.setStyle
(
el.
nativeElement,
'color'
,
'red'
);
}
@HostListener
(
'mouseenter'
) onMouseEnter
(
) {
console.log
(
'mouseenter'
)
}
@HostListener
(
'mouseleave'
) onMouseLeave
(
) {
console.log
(
'mouseleave'
)
}
}
À l'exécution, si l'on ouvre la fenêtre de debug JavaScript, on remarque que notre div réagit bien aux évènements mouseenter et mouseleave.
Il ne nous reste plus qu'à définir la couleur paramétrable grâce à un autre décorateur Angular : le décorateur @Input. Ce décorateur permet de lier le paramètre passé à une propriété de la classe de la Directive. Il est possible de passer en paramètre un alias dans le cas où la propriété de la classe n'a pas le même que l'attribut de la Directive. Voici notre Directive finale :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
import {
Directive,
Renderer2,
ElementRef,
HostListener,
Input}
from '@angular/core'
;
@Directive
({
selector
:
'[appHighlight]'
}
)
export class HighlightDirective {
@Input
(
'appHighlight'
) highlightColor
:
string;
private _defaultColor =
'red'
;
constructor
(
private _el
:
ElementRef,
private _renderer
:
Renderer2) {
this.
_renderer.setStyle
(
this.
_el.
nativeElement,
'color'
,
this.
_defaultColor);
}
@HostListener
(
'mouseenter'
) onMouseEnter
(
) {
this.
_renderer.setStyle
(
this.
_el.
nativeElement,
'color'
,
this.
highlightColor);
}
@HostListener
(
'mouseleave'
) onMouseLeave
(
) {
this.
_renderer.setStyle
(
this.
_el.
nativeElement,
'color'
,
this.
_defaultColor);
}
}
Grâce à ce code, il est possible d'utiliser notre Directive comme ceci :
<div [appHighlight]=
"'yellow'"
>
Texte en highlight</div>
La classe a été un peu remaniée pour coller au besoin :
- Le constructeur a été modifié pour permettre d'utiliser ses paramètres dans toute la classe. Le fait de mettre la portée devant les paramètres est un accélérateur de développement qui permet de faire l'équivalent d'un this._el = _el. C'est une spécificité TypeScript bien pratique.
- Le @Input a été ajouté. Il permet de récupérer notre couleur passée en paramètre de notre directive.
- Nos @HostListener ont été modifiés pour permettre le changement de couleur comme c'était le cas avec notre constructeur.
Voilà, maintenant, vous êtes prêts à faire vos propres Directives Angular !