Les directives structurelles ?

Ce guide explique comment Angular manipule le DOM avec des directives structurelles et comment vous pouvez écrire vos propres directives structurelles pour faire la même chose.

Que sont les directives structurelles ?

Les directives structurelles sont responsables de la mise en page HTML. Ils façonnent ou remodèlent la structure du DOM, généralement en ajoutant, en supprimant ou en manipulant des éléments.

Comme pour les autres directives, vous appliquez une directive structurelle à un élément hôte. La directive fait ensuite ce qu'elle est supposée faire avec cet élément hôte et ses descendants.

Les directives structurelles sont faciles à reconnaître. Un astérisque ( * ) précède le nom de l'attribut de directive comme dans cet exemple.

Pas de crochets, pas de parenthèses, il suffit de définir une chaîne *ngIf. Vous apprendrez dans ce guide que l'astérisque ( * ) est une notation pratique et que la chaîne est une microsyntaxe plutôt que l'expression de modèle habituelle. Dans Angular cette notation dans un balisage qui entoure l'élément hôte et ses descendants. Chaque directive structurelle fait quelque chose de différent avec ce modèle.

Voici un exemple dans un modèle des trois des directives structurelles intégrées courantes NgIf , NgFor et NgSwitch.

Tout au long de ce guide, vous verrez une directive épelée à la fois par UpperCamelCase et lowerCamelCase. Vous avez déjà vu NgIf et ngIf. Il y a une raison. NgIf fait référence à la classe de directive ; ngIf fait référence au nom d'attribut de la directive.

Une classe de directive est orthographiée dans UpperCamelCase (NgIf). Le nom d'attribut d'une directive est orthographié dans lowerCamelCase (ngIf). Le guide fait référence à la classe de directive quand on parle de ses propriétés et de ce que fait la directive. Le guide fait référence au nom de l' attribut lorsque vous décrivez comment vous appliquez la directive à un élément du modèle HTML.

Il existe deux autres types de directives Angular :
  • des composants
  • des directives d'attribut

Un composant gère une région HTML à la manière d'un élément HTML natif. Techniquement, c'est une directive avec un modèle.

Une directive d'attribut change l'apparence ou le comportement d'un élément, d'un composant ou d'une autre directive. Par exemple, la directive NgStyle intégrée modifie plusieurs styles d'élément en même temps.

Vous pouvez appliquer de nombreuses directives d'attribut à un élément hôte. Vous ne pouvez appliquer qu'une directive structurelle à un élément hôte.

Etude de cas NgIf

NgIf est la directive structurelle la plus simple et la plus facile à comprendre. Elle prend une expression booléenne et fait apparaître ou disparaître tout un morceau du DOM.

La directive ngIf ne cache pas les éléments avec CSS. Il les ajoute et les supprime physiquement du DOM. Confirmez ce fait à l'aide des outils de développement de navigateur pour inspecter le DOM.


Le premier paragraphe est dans le DOM. Le paragraphe en bas, désaffecté n'y est pas; à sa place se trouve un commentaire sur les "bindings".

Lorsque la condition est false, NgIf supprime l'élément du DOM, le dissocie des événements du DOM, dissocie le composant de la détection des modifications Angular et le détruit. Les composants et les noeuds DOM peuvent être récupérés et libérer de la mémoire.

Pourquoi enlever plutôt que se cacher ?

Une directive pourrait plutôt masquer le paragraphe indésirable en définissant son style.display sur none.
Bien qu'invisible, l'élément reste dans le DOM.


La différence entre cacher et enlever importe peu pour un simple paragraphe. Cela importe quand l'élément hôte est attaché à un composant gourmand en ressources. Le comportement d'un tel composant se poursuit même lorsqu'il est masqué. Le composant reste attaché à son élément DOM. Il continue à écouter les événements. Angular continue de rechercher les modifications susceptibles d'affecter les liaisons de données.

Bien qu'invisible, le composant et tous ses composants descendants lient des ressources. Les performances et la charge de mémoire peuvent être considérables, la réactivité peut se dégrader et l'utilisateur ne voit rien.

Du côté positif, montrer à nouveau l'élément est rapide. L'état précédent du composant est préservé et prêt à être affiché. Le composant ne se réinitialise pas, une opération qui pourrait être coûteuse. Donc, se cacher et se montrer est parfois la bonne chose à faire.

Mais en l'absence d'une raison impérieuse de les conserver, votre préférence devrait être de supprimer les éléments du DOM que l'utilisateur ne peut pas voir et de récupérer les ressources inutilisées avec une directive structurelle telle que NgIf.

Ces mêmes considérations s'appliquent à toutes les directives structurelles, qu'elles soient intégrées ou personnalisées. Avant d'appliquer une directive structurelle, vous pouvez vous arrêter un instant pour examiner les conséquences de l'ajout et de la suppression d'éléments, ainsi que de la création et de la destruction de composants.


Le préfixe astérisque (*)

Vous avez sûrement remarqué le préfixe astérisque ( * ) du nom de la directive et vous vous demandez pourquoi il est nécessaire et ce qu'il fait.
L'astérisque est l'élément syntaxique pour quelque chose d'un peu plus compliqué. En interne, Angular traduit l'attribut *ngIf en un élément ‹ng-template›, enroulé autour de l'élément hôte, comme ceci.
  • La directive *ngIf a été déplacée vers l'élément ‹ng-template› ou elle est devenue une liaison de propriété, [ngIf].
  • Le reste du ‹div›, y compris son attribut de classe, s'est déplacé à l'intérieur de l'élément .
Le premier formulaire n'est pas réellement rendu, seul le produit fini se retrouve dans le DOM.


Angular a consommé le ‹ng-template› contenu lors de son rendu réel et l'a remplacé par un commentaire de diagnostic. Les directives NgFor et NgSwitch suivent le même schéma.

À l'intérieur du *ngFor

Angular transforme le *ngFor de la même manière que la syntaxe astérisque ( * ) en élément ‹ng-template›.

Voici une application complète de NgFor, écrit dans les deux sens :

Ceci est manifestement plus compliqué que ngIf et à juste titre. La directive NgFor comporte plus de fonctionnalités, obligatoires et facultatives, que la directive NgIf présentée. Au minimum, NgFor a besoin d'une variable en boucle (let hero) et d'une liste (heroes).

Vous activez ces fonctionnalités dans la chaîne affectée ngFor, que vous écrivez dans la microsyntaxe d'Angular.

Tout ce qui se trouve en dehors de la chaîne ngFor reste dans l'élément hôte (‹div›) à mesure qu'il se déplace à dans le ‹ng-template›. Dans cet exemple, la [ngClass]="odd" reste sur le ‹div› .

Microsyntax

La microsyntaxe Angular vous permet de configurer une directive dans une chaîne compacte et conviviale.
L'analyseur de microsyntaxe traduit cette chaîne en attributs sur le ‹ng-template› :

  • Le mot clé let déclare une variable d'entrée de modèle à laquelle vous faites référence dans le modèle. Les variables d'entrée dans cet exemple sont hero, i et odd. L'analyseur traduit let hero, let i et let odd en variables appelées, let-hero, let-i et let-odd.

  • L'analyseur de microsyntaxe prend of et trackBy (of -> Of, trackBy -> TrackBy) et les préfixe avec le nom d'attribut de la directive ( ngFor ), ce qui donne les noms ngForOf et ngForTrackBy. Ce sont les noms de deux propriétés d'entrée NgFor. C'est ainsi que la directive apprend que la liste est constituée de héros et que la fonction de suivi par piste est trackById.

  • Lorsque la directive NgFor parcourt la liste, elle définit et réinitialise les propriétés de son propre objet de contexte. Ces propriétés incluent index et odd et une propriété spéciale nommée $implicit.

  • Les variables let-i et let-odd sont définies comme suit : let i=index et let odd=odd. Angular les définit sur la valeur actuelle de index et des propriétés odd.

  • La propriété de contexte pour let-hero n'a pas été spécifiée. Sa source prévue est implicite. Angular définit let-hero sur la valeur de la propriété $implicit du contexte que NgFor a initialisée avec le heros pour l'itération en cours.

  • Le guide de l'API décrit d'autres propriétés de directive NgFor et de contexte.

  • NgFor est mis en oeuvre par la directive NgForOf. Pour en savoir plus sur les propriétés de directive NgForOf supplémentaires et les propriétés de contexte, référence de l'API NgForOf.

Ces mécanismes de microsyntaxe sont à votre disposition lorsque vous écrivez vos propres directives structurelles. L'étude du code source NgIf et NgForOf est un excellent moyen d'en savoir plus.

Modèle d'entrée variable

Une variable d'entrée de modèle est une variable dont vous pouvez référencer la valeur dans une seule instance du modèle. Il existe plusieurs variables dans cet exemple: hero, i et odd. Tous sont précédés du mot clé let.

Une variable d'entrée de modèle n'est pas la même chose qu'une variable de référence de modèle, ni sémantiquement ni syntaxiquement.

Vous déclarez une variable d'entrée de modèle à l'aide du mot clé let (let hero). La portée de la variable est limitée à une seule instance du modèle. Vous pouvez utiliser à nouveau le même nom de variable dans la définition d'autres directives structurelles.

Vous déclarez une variable de référence de modèle en préfixant le nom de la variable avec # ( #var ). Une variable de référence fait référence à son élément, composant ou directive attaché. Vous pouvez y accéder n'importe où dans le modèle entier.

Les noms de variable d'entrée et de référence de modèle ont leurs propres espaces de noms. Le hero de let hero n'est pas la même variable que le hero déclaré comme #hero.

Une directive structurelle par élément hôte

Un jour, vous voudrez peut-être répéter un bloc HTML, mais uniquement lorsqu'une condition particulière est vraie. Vous allez essayer de mettre un *ngFor et un *ngIf sur le même élément hôte. Angular ne vous laissera pas faire. Vous ne pouvez appliquer qu'une seule directive structurelle à un élément.

La raison est la simplicité. Les directives structurelles peuvent faire des choses complexes avec l'élément hôte et ses descendants. Lorsque deux directives revendiquent le même élément hôte, lequel des deux est prioritaire ? Qui devrait aller en premier, le NgIf ou le NgFor ? Le NgIf peut-il annuler l'effet du NgFor ? Si tel est le cas (et il semble que cela devrait être le cas), comment Angular devrait-il généraliser la possibilité d'annuler d'autres directives structurelles ?

Il n'y a pas de réponse facile à ces questions. L'interdiction de plusieurs directives structurelles les rend inutiles. Il existe une solution simple pour ce cas d'utilisation: mettez l'élément *ngIf sur un conteneur qui enveloppe l'élément *ngFor. Un ou les deux éléments peuvent être un ng-container afin de ne pas avoir à introduire de niveaux supplémentaires de HTML.

Dans les directives de NgSwitch

Dans Angular NgSwitch est en fait un ensemble de directives de coopération: NgSwitch, NgSwitchCaseet NgSwitchDefault. Voici un exemple :
La valeur de commutation affectée à NgSwitch ( hero.emotion ) détermine le cas échéant des commutations affichés.

NgSwitch elle-même n'est pas une directive structurelle. C'est une directive attribut qui contrôle le comportement des deux autres directives switch. C'est pourquoi vous écrivez [ngSwitch], jamais *ngSwitch.

NgSwitchCase et NgSwitchDefault sont des directives structurelles. Vous les attachez à des éléments en utilisant la notation de préfixe astérisque ( * ). Un NgSwitchCase affiche son élément hôte lorsque sa valeur correspond à la valeur du commutateur. Le NgSwitchDefault affiche son élément hôte lorsqu'aucun NgSwitchCase ne correspond à la valeur du commutateur.

L'élément auquel vous appliquez une directive est son élément hôte . Le ‹happy-hero› est l'élément d'accueil pour les *ngSwitchCase. Le ‹unknown-hero› est l'élément hôte pour le *ngSwitchDefault.

Comme avec les autres directives structurelles, le NgSwitchCase et NgSwitchDefault peuvent être supprimés dans le ‹ng-template›.



Préférez la syntaxe astérisque(*)

La syntaxe astérisque ( * ) est plus claire que la forme désaspérée. Utilisez ‹ng-container› lorsqu'il n'y a pas un seul élément pour héberger la directive.

Bien qu'il y ait rarement une bonne raison d'appliquer une directive structurelle sous forme d'attribut ou d'élément de modèle, il est toujours important de savoir qu'Angular crée un ‹ng-template› et comprend son fonctionnement. Vous vous référerez à la ‹ng-template› lorsque vous écrivez votre propre directive structurelle.

Le ‹ng-template›

Le ‹ng-template› est un élément Angular pour le rendu HTML. Il n'est jamais affiché directement. En fait, avant de rendre la vue, Angular remplace le ‹ng-template› et son contenu par un commentaire.

S'il n'y a pas de directive structurelle et que vous ajoutez simplement certains éléments dans un ‹ng-template›, ces éléments disparaissent.


Angular efface le milieu "Hip!".



Regrouper les éléments frères avec ‹ng-container›

Il existe souvent un élément fondamental qui peut et devrait héberger la directive structurelle. L'élément list ( ‹li› ) est un élément hôte typique d'un répéteur NgFor.

Lorsqu'il n'y a pas d'élément hôte, vous pouvez généralement envelopper le contenu dans un élément de conteneur HTML natif, tel qu'un ‹div›, et attacher la directive à cet encapsuleur.

L'introduction d'un autre élément conteneur, généralement un ‹span› ou, ‹div› pour regrouper les éléments sous une seule racine, est généralement sans danger. Habituellement, mais pas toujours.

L'élément de regroupement peut altérer l'apparence du modèle car les styles CSS n'attendent ni ne prennent en charge la nouvelle présentation. Par exemple, supposons que vous disposiez de la disposition de paragraphe suivante.

Vous avez également une règle de style CSS qui s'applique à ‹span› ou un paragraph ‹p›.

Le paragraphe construit rend étrangement.


Le style p span, destiné à être utilisé ailleurs, a été appliqué ici par inadvertance.

Autre problème: certains éléments HTML nécessitent que tous les enfants immédiats soient d'un type spécifique. Par exemple, l'élément ‹select› nécessite des enfants ‹option›. Vous ne pouvez pas envelopper les options dans un conditionnel ‹div› ou un ‹span›.

La liste déroulante est vide, le navigateur ne va pas afficher un ‹option› dans un ‹span›.



‹ng-container› à la rescousse

Le ‹ng-container› est un élément de regroupement qui n'interfère pas avec les styles ou la mise en page, car Angular ne le met pas dans le DOM. Voici le paragraphe conditionnel, cette fois en utilisant ‹ng-container›.

Il rend correctement.


Maintenant, exclure sous condition une sélection ‹option› avec ‹ng-container›.

La liste déroulante fonctionne correctement.


Le ‹ng-container› est un élément de syntaxe reconnu par l'analyseur Angular. Ce n'est pas une directive, un composant, une classe ou une interface. Cela ressemble plus aux accolades d'un bloc if JavaScript :

Sans ces accolades, JavaScript n'exécuterait la première instruction que si vous avez l'intention de les exécuter de manière conditionnelle en un seul bloc. Le ‹ng-container› répond à un besoin similaire dans les modèles Angular.

Écrire une directive structurelle

Dans cette section, vous écrivez une directive UnlessDirective structurelle qui fait le contraire de NgIf. NgIf affiche le contenu du modèle lorsque la condition est true. UnlessDirective affiche le contenu lorsque la condition est false.
La création d'une directive est similaire à la création d'un composant.

  • Importez le décorateur Directive (au lieu du décorateur Component).
  • Importer le Input, TemplateRef et les symboles ViewContainerRef; vous en aurez besoin pour toute directive structurelle.
  • Appliquez le décorateur à la classe de directive.
  • Définissez le sélecteur d'attribut CSS qui identifie la directive lorsqu'elle est appliquée à un élément dans un modèle.

Voici comment vous pourriez commencer :

Le sélecteur de la directive est généralement le nom de l'attribut de la directive entre crochets [appUnless]. Les crochets définissent un sélecteur d'attribut CSS.

Le nom de l'attribut de directive doit être orthographié dans lowerCamelCase et commencer par un préfixe. Ne pas utiliser ng. Ce préfixe appartient à Angular. Choisissez quelque chose de court qui vous convient ou à votre entreprise. Dans cet exemple, le préfixe est app.

Le nom de la classe de directive se termine par Directive le guide de style. Les propres directives d'Angular ne le font pas.

TemplateRef et ViewContainerRef

Une directive structurelle simple comme celle-ci crée une vue intégrée à partir de la source Angular générée ‹ng-template› et insère cette vue dans un conteneur de vues adjacent à l'élément ‹p› hote d'origine de la directive.

Vous obtiendrez le contenu de ‹ng-template› avec TemplateRef et accéderez au conteneur de vues par le biais de ViewContainerRef.

Vous injectez les deux dans le constructeur de directive en tant que variables privées de la classe.

La propriété appUnless

Le consommateur de directives s'attend à lier une condition true / false à [appUnless]. Cela signifie que la directive a besoin d'une propriété appUnless, décorée avec @Input.

Angular définit la propriété appUnless chaque fois que la valeur de la condition change. Parce que la propriété appUnless fonctionne, il faut un passeur.

  • Si la condition est falsifiée et que la vue n'a pas encore été créée, indiquez au conteneur de vues de créer la vue intégrée à partir du modèle.
  • Si la condition est véridique et que la vue est actuellement affichée, effacez le conteneur qui détruit également la vue.
Personne ne lit la propriété appUnless, elle n'a donc pas besoin d'un getter.

Le code de directive complété :

Ajoutez cette directive au tableau declarations de l'AppModule. Puis créez du HTML pour l'essayer.


Lorsque le message condition est false, le paragraphe supérieur ( A ) apparaît et le paragraphe ( B ) disparaît. Lorsque la conditionvérité est trouvée, le paragraphe ( A ) est supprimé et le paragraphe ( B ) apparaît.