Maîtriser les directives AngularJS

Les directives sont l’un des composants les plus puissants d’AngularJS. Elles vous aident à étendre les éléments / attributs HTML de base et à créer réutilisable et testable code. Dans ce tutoriel, je vais vous montrer comment utiliser les directives AngularJS avec les meilleures pratiques réelles.. 

Ce que je veux dire par ici les directivesest principalement des directives personnalisées lors du tutoriel. Je ne vais pas essayer de vous apprendre à utiliser les directives intégrées telles que ng-répéter, ng-show, etc. Je vais vous montrer comment utiliser les directives personnalisées pour créer vos propres composants.. 

Contour

  1. Directives simples
  2. Restrictions de la directive
  3. Portée isolée
  4. Champ d'application de la directive
  5. Directive Héritage
  6. Directive de débogage
  7. Test unitaire directive
  8. Test de portée de la directive
  9. Conclusion

1. Directives simples

Supposons que vous avez une application de commerce électronique sur les livres et que vous affichez des détails de livre spécifiques dans plusieurs domaines, tels que les commentaires, les pages de profil utilisateur, les articles, etc. Votre widget de détail de livre peut ressembler à celui ci-dessous:

Ce widget contient une image de livre, un titre, une description, des commentaires et une évaluation. Il peut être difficile de collecter ces informations et de mettre en place un élément dom spécifique à l’endroit où vous souhaitez l’utiliser. Voyons cette vue à l'aide d'une directive AngularJS. 

angular.module ('masteringAngularJsDirectives', []) .directive ('book', function () return restreindre: 'E', étendue: data: '=', templateUrl: 'templates / book-widget.html ')

Une fonction de directive a été utilisée dans l'exemple ci-dessus pour créer une directive en premier. Le nom de la directive est livreCette directive retourne un objet, et parlons un peu de cet objet. restreindreest pour définir le type de directive, et il peut être UNE(UNEtribute),C(Cfille), E (Element), etM(coMment). Vous pouvez voir l'utilisation de chacun respectivement ci-dessous.

Type Usage
UNE
livre>
C
E <livre data = "book_data">livre>
M

portéeest destiné à gérer le champ d'application de la directive. Dans le cas ci-dessus, les données de livre sont transférées dans le modèle de directive à l’aide de la touche "="type de portée. Je parlerai en détail de la portée dans les sections suivantes. templateUrlest utilisé pour appeler une vue afin de rendre un contenu spécifique à l'aide de données transférées dans le champ d'application de la directive. Vous pouvez aussi utiliser modèleet fournissez le code HTML directement, comme ceci:

… modèle: '
Livre Info
'…

Dans notre cas, nous avons une structure HTML compliquée, et c’est pourquoi j’ai choisi la templateUrloption.

2. Restrictions de la directive

Les directives sont définies dans le fichier JavaScript de votre projet AngularJS et utilisées dans la page HTML. Il est possible d’utiliser les directives AngularJS dans les pages HTML comme suit:

A (attribut)

Dans cette utilisation, le nom de la directive est utilisé dans des éléments HTML standard. Disons que vous avez un menu basé sur les rôles dans votre application de commerce électronique. Ce menu est formé en fonction de votre rôle actuel. Vous pouvez définir une directive pour décider si le menu actuel doit être affiché ou non. Votre menu HTML peut être comme ci-dessous:

  • Accueil
  • Dernières nouvelles
  • Administration des utilisateurs
  • Gestion de campagne

et la directive comme suit:

app.directive ("restricted", function () return restrict: 'A', lien: function (portée, élément, attrs) // Une fonction de contrôle d'authentification var isAuthorized = checkAuthorization (); if (! isAuthorized)  element.css ('display', 'none');)

Si vous utilisez le limitéattribut, vous pouvez effectuer une vérification du niveau d’accès pour chaque menu. Si l'utilisateur actuel n'est pas autorisé, ce menu spécifique ne sera pas affiché. 

Alors, quel est le lienfonctionner là-bas? Simplement, la fonction de liaison est la fonction que vous pouvez utiliser pour effectuer des opérations spécifiques à une directive. La directive ne rend pas seulement du code HTML en fournissant des entrées. Vous pouvez également lier des fonctions à l'élément de directive, appeler un service et mettre à jour la valeur de la directive, obtenir les attributs de la directive s'il s'agit d'un élément. Edirective de type, etc..

C (classe)

Vous pouvez utiliser le nom de la directive à l'intérieur des classes d'élément HTML. En supposant que vous utilisiez la directive ci-dessus comme Cvous pouvez mettre à jour la directive restreindre comme Cet l'utiliser comme suit:

  • Accueil
  • Dernières nouvelles
  • Administration des utilisateurs
  • Gestion de campagne

Chaque élément a déjà une classe pour le style, et comme limité la classe est ajoutée, il s'agit en fait d'une directive.

E (élément)

Vous n'avez pas besoin d'utiliser une directive dans un élément HTML. Vous pouvez créer votre propre élément en utilisant une directive AngularJS avec un E restriction. Disons que vous avez un widget utilisateur dans votre application pour montrer Nom d'utilisateur, avatar, et réputationà plusieurs endroits dans votre application. Vous voudrez peut-être utiliser une directive comme celle-ci:

app.directive ("user", function () return restrict: 'E', lien: function (portée, élément, attrs) scope.username = attrs.username; scope.avatar = attrs.avatar; scope.reputation = attrs.reputation;, template: '
Nom d'utilisateur: nom d'utilisateur, Avatar: avatar, Réputation: réputation
')

Le code HTML sera:

Dans l'exemple ci-dessus, un élément personnalisé est créé et certains attributs sont fournis comme Nom d'utilisateur, avatar, et réputation. Je veux attirer l'attention sur le corps de la fonction de lien. Les attributs d'élément sont affectés à la portée de la directive. Le premier paramètre de la fonction de liaison est la portée de la directive actuelle. Le troisième paramètre de la directive est l'objet attribut de la directive, ce qui signifie que vous pouvez lire n'importe quel attribut de la directive custom en utilisant attrs.attr_nom. Les valeurs d'attribut sont attribuées à l'étendue afin qu'elles soient utilisées dans le modèle. 

En fait, vous pouvez faire cette opération de manière plus courte, et j'en parlerai plus tard. Cet exemple est pour comprendre l'idée principale derrière l'utilisation.

M (engagement)

Cet usage n'est pas très courant, mais je vais vous montrer comment l'utiliser. Supposons que vous ayez besoin d'un formulaire de commentaire pour votre application à utiliser dans de nombreux endroits. Vous pouvez le faire en utilisant la directive suivante:

app.directive ("comment", function () return restrict: 'M', template: '')

Et dans l'élément HTML:

3. Portée isolée

Chaque directive a son propre champ d'application, mais vous devez faire attention à la liaison de données avec la déclaration de directive. Disons que vous mettez en œuvre le panierpartie de votre application de commerce électronique. Sur la page du panier, vous avez déjà ajouté des articles ici. Chaque article a son champ quantité pour sélectionner le nombre d'articles que vous voulez acheter, comme ci-dessous:

Voici la déclaration de directive:

app.directive ("item", function () return restreindre: 'E', lien: fonction (scope, élément, attrs) scope.name = attrs.name;, template: '
Prénom: prénom Sélectionnez le montant: Montant sélectionné: compter
')

Et pour afficher trois éléments en HTML:

  

Le problème ici est que chaque fois que vous choisissez le montant de l'élément souhaité, toutes les sections de montant des éléments seront mises à jour. Pourquoi? Parce qu'il existe une liaison de données bidirectionnelle avec un nom compter, mais la portée n'est pas isolée. Afin d'isoler la portée, il suffit d'ajouter portée: à l'attribut directive dans la section return:

app.directive ("item", function () return restreindre: 'E', étendue: , lien: fonction (portée, élément, attrs) scope.name = attrs.name;, template: '
Prénom: prénom Sélectionnez le montant: Montant sélectionné: compter
')

Cela amène votre directive à disposer de sa propre portée isolée afin qu'une liaison de données bidirectionnelle se produise séparément dans cette directive. Je vais aussi parler de la portéeattribuer plus tard.

4. Champ d'application de la directive

Le principal avantage de la directive est qu’il s’agit d’un composant réutilisable qui peut être utilisé facilement. Vous pouvez même fournir des attributs supplémentaires à cette directive. Mais, comment est-il possible de transmettre une valeur, une liaison ou une expression supplémentaire à une directive afin que les données puissent être utilisées à l'intérieur de la directive?

"@" Portée: Ce type de portée est utilisé pour transmettre une valeur à la portée de la directive. Supposons que vous souhaitiez créer un widget pour un message de notification:

app.controller ("MessageCtrl", function () $ scope.message = "Produit créé!";) app.directive ("notification", function () return restrict: 'E', étendue: message: '@' , modèle: '
message
');

et vous pouvez utiliser:

Dans cet exemple, la valeur du message est simplement affectée à la portée de la directive. Le contenu HTML rendu sera:

Produit créé!

"=" Portée: Dans ce type de portée, les variables de portée sont passées à la place des valeurs, ce qui signifie que nous ne passerons pas message, nous allons passer message au lieu. La raison de cette fonctionnalité est la construction d’une liaison de données bidirectionnelle entre la directive et les éléments de page ou les contrôleurs. Voyons le en action.

.directive ("bookComment", function () return restrict: 'E', étendue: text: '=', template: '')

Dans cette directive, nous essayons de créer un widget pour afficher la saisie de texte de commentaire afin de commenter un livre spécifique. Comme vous pouvez le constater, cette directive nécessite un attribut texte construire une liaison de données bidirectionnelle entre les autres éléments des pages. Vous pouvez l'utiliser sur la page:

Ceci est la zone de texte sur la directive 

Cela affichera simplement une zone de texte sur la page, ajoutons donc quelque chose de plus pour interagir avec cette directive:

Ceci est la zone de texte sur la page  
Ceci est la zone de texte sur la directive

Chaque fois que vous tapez quelque chose dans la première zone de texte, il sera également saisi dans la deuxième zone de texte. Vous pouvez faire cela vice versa. Dans la directive, nous avons passé la variable scope commentText au lieu de la valeur, et cette variable est la référence de liaison de données à la première zone de texte. 

"&" Portée: Nous sommes en mesure de transmettre la valeur et la référence aux directives. Dans ce type de portée, nous verrons comment passer des expressions à la directive. Dans les cas réels, vous devrez peut-être transmettre une fonction spécifique (expression) aux directives afin d'empêcher le couplage. Parfois, les directives n'ont pas besoin d'en savoir beaucoup sur l'idée qui sous-tend les expressions. Par exemple, une directive vous plaira, mais elle ne sait pas comment faire. Pour ce faire, vous pouvez suivre une structure comme celle-ci:

.directive ("likeBook", function () return restreindre: 'E', étendue: comme: '&', template: '')

Dans cette directive, une expression sera transmise au bouton de directive via la commande comme attribut. Définissons une fonction dans le contrôleur et la passons à la directive à l'intérieur du code HTML.

$ scope.likeFunction = function () alert ("J'aime le livre!")

Ce sera à l'intérieur du contrôleur, et le modèle sera:

likeFunction () vient du contrôleur et est passé à la directive. Et si vous voulez passer un paramètre à likeFunction ()? Par exemple, vous devrez peut-être attribuer une valeur d'évaluation au likeFunction (). C'est très simple: ajoutez simplement un argument à la fonction à l'intérieur du contrôleur, puis ajoutez un élément input à la directive pour exiger le nombre de débuts de l'utilisateur. Vous pouvez le faire comme indiqué ci-dessous:

.directive ("likeBook", function () return restreindre: 'E', étendue: comme: '&', template: '
'+'')
$ scope.likeFunction = fonction (étoile) alert ("J'aime le livre !, et a donné" + étoile + "étoile.")

Comme vous pouvez le constater, la zone de texte provient de la directive. La valeur de la zone de texte est liée à l'argument de la fonction comme comme (star: starCount). étoile est pour la fonction de contrôleur, et starCount pour la liaison de valeur de zone de texte. 

5. Héritage Directive

Parfois, vous pouvez avoir une fonctionnalité qui existe dans plusieurs directives. Ils peuvent être placés dans une directive parent afin qu'ils soient hérités par les directives enfant. 

Laissez-moi vous donner un exemple concret. Vous souhaitez envoyer des données statistiques chaque fois que les clients déplacent le curseur de leur souris vers le haut d'un livre spécifique. Vous pouvez implémenter un événement de clic de souris pour la directive book, mais que se passe-t-il s'il sera utilisé par une autre directive? Dans ce cas, vous pouvez utiliser l'héritage des directives comme ci-dessous:

app.directive ('mouseClicked', function () return restreindre: 'E', étendue: , contrôleur: "MouseClickedCtrl as mouseClicked")

Il s'agit d'une directive parent à hériter des directives enfant. Comme vous pouvez le constater, un attribut controller de la directive utilise la directive "as". Définissons également ce contrôleur:

app.controller ('MouseClickedCtrl', function ($ element) var mouseClicked = this; mouseClicked.bookType = null; mouseClicked.setBookType = fonction (type) mouseClicked.bookType = type; $ element.bind ("clic", function () alert ("Type de livre:" + mouseClicked.bookType + "envoyé pour analyse statistique!");))

Dans ce contrôleur, nous définissons simplement une instance de contrôleur de la variable bookType en utilisant des directives enfants. Chaque fois que vous cliquez sur un livre ou un magazine, le type d'élément sera envoyé au service d'arrière-plan (j'ai utilisé une fonction d'alerte uniquement pour afficher les données). Comment les directives enfants pourront-elles utiliser cette directive??

app.directive ('ebook', function () return require: "mouseClicked", lien: function (portée, élément, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("EBOOK");) .directive (' magazine ', function () return require: "mouseClicked", lien: function (portée, élément, attrs, mouseClickedCtrl) mouseClickedCtrl.setBookType ("MAGAZINE");)

Comme vous pouvez le constater, les directives enfants utilisent le exiger mot-clé pour utiliser la directive parent. Et un autre point important est le quatrième argument de la fonction de liaison dans les directives enfants. Cet argument fait référence à l'attribut controller de la directive parent, ce qui signifie que la directive enfant peut utiliser la fonction controller setBookType à l'intérieur du contrôleur. Si l'élément en cours est un livre électronique, vous pouvez utiliser la première directive, et s'il s'agit d'un magazine, vous pouvez utiliser la seconde:

Jeu de trônes (cliquez-moi)
PC World (cliquez-moi)

Les directives enfants sont comme une propriété de la directive parent. Nous avons éliminé l'utilisation de l'événement clic de souris pour chaque directive enfant en plaçant cette section dans la directive parent..

6. Débogage de la directive

Lorsque vous utilisez des directives dans le modèle, vous voyez sur la page la version compilée de la directive. Parfois, vous voulez voir l'utilisation réelle de la directive à des fins de débogage. Pour voir la version non compilée de la section en cours, vous pouvez utiliser ng-non-bindable. Par exemple, disons que vous avez un widget qui imprime les livres les plus populaires, et voici le code pour cela:

La variable de portée du livre provient du contrôleur, et le résultat est le suivant:

Si vous voulez connaître l'utilisation de la directive derrière cette sortie compilée, vous pouvez utiliser cette version du code:

  • livre

Cette fois, le résultat sera comme ci-dessous:

Jusqu'ici, c'est cool, mais qu'en est-il si nous voulons voir les versions non compilées et compilées du widget? Il est temps d'écrire une directive personnalisée qui effectuera une opération de débogage avancée.. 

app.directive ('customDebug', function ($ compile) return terminal: true, lien: function (portée, élément) var currentElement = element.clone (); currentElement.removeAttr ("custom-debug"); var newElement = $ compile (currentElement) (portée); element.attr ("style", "border: 1px solid red"); element.after (newElement);)

Dans cette directive, nous clonons l'élément qui est en mode débogage afin qu'il ne soit pas modifié après un ensemble d'opérations. Après le clonage, retirez le débogage personnalisédirective afin de ne pas agir en mode débogage, puis compilez-le avec $ complile, qui est déjà injecté dans la directive. Nous avons donné un style à l’élément de mode débogage pour mettre l’accent sur celui qui est débogué. Le résultat final sera comme ci-dessous:

Vous pouvez économiser votre temps de développement en utilisant ce type de directive de débogage pour détecter la cause première de toute erreur dans votre projet..

7. Test unitaire directive

Comme vous le savez déjà, les tests unitaires sont une partie très importante du développement pour contrôler totalement le code que vous avez écrit et éviter les bugs potentiels. Je ne plongerai pas dans les tests unitaires, mais je vous donnerai un indice sur la manière de tester les directives de plusieurs manières..

Je vais utiliser Jasmine pour les tests unitaires et Karma pour le coureur de tests unitaires. Pour utiliser Karma, installez-le simplement de manière globale en exécutant npm installer -g karma karma-cli (Node.js et npm doivent être installés sur votre ordinateur). Après l’installation, ouvrez la ligne de commande, accédez au dossier racine de votre projet et tapez karma init. Il vous posera quelques questions comme ci-dessous afin de définir vos exigences de test..

J'utilise Webstorm pour le développement, et si vous utilisez également Webstorm, faites un clic droit sur karma.conf.js et sélectionnez Courir karma.conf.js. Ceci exécutera tous les tests configurés dans la configuration du karma. Vous pouvez également exécuter des tests avec le début de karma ligne de commande dans le dossier racine du projet. Tout dépend de la configuration de l'environnement, passons donc à la partie test.

Disons que nous voulons tester la directive livre. Lorsque nous transmettons un titre à la directive, celle-ci doit être compilée dans une vue détaillée du livre. Alors, commençons.

describe ("Book Tests", function () var élément; var scope; beforeEach (module ("masteringAngularJsDirectives")) beforeEach (inject (fonction ($ compile, $ rootScope) scope = $ rootScope; element = angular.element ( ""); $ compile (element) ($ rootScope) scope. $ digest ())); it (" la directive doit être compilée avec succès ", function () expect (element.html ()). toBe (" test " )));

Dans le test ci-dessus, nous testons une nouvelle directive appelée test de livreCette directive prend l'argument Titreet crée une div en utilisant ce titre. Dans le test, avant chaque section de test, nous appelons notre module masteringAngularJsDirectivespremier. Ensuite, nous générons une directive appelée test de livre. À chaque étape de test, la sortie de directive sera testée. Ce test est juste pour un contrôle de valeur.

8. Test de portée de la directive

Dans cette section, nous allons tester le champ d'application de la directive test de livreCette directive génère une vue détaillée du livre sur la page. Lorsque vous cliquez sur cette section détaillée, une variable d’étendue appelée vusera défini comme vrai. Dans notre test, nous vérifierons si vuest défini sur true lorsque l'événement click est déclenché. La directive est:

.directive ('booktest', function () return restreindre: 'E', étendue: titre: '@', remplacer: vrai, modèle: '
Titre
', lien: fonction (portée, élément, attrs) element.bind ("clic", fonction () console.log ("livre consulté!"); scope.viewed = true;); )

Pour définir un événement sur un élément dans AngularJS dans la directive, vous pouvez utiliser le lien attribut. Dans cet attribut, vous avez l'élément actuel, directement lié à un événement click. Afin de tester cette directive, vous pouvez utiliser les éléments suivants:

describe ("Book Tests", function () var élément; var scope; beforeEach (module ("masteringAngularJsDirectives")) beforeEach (inject (fonction ($ compile, $ rootScope) scope = $ rootScope; element = angular.element ( ""); $ compile (élément) ($ rootScope) portée. $ digest ())); it (" portée aimée devrait être vraie quand livre aimé ", function () element.triggerHandler (" click "); expect ( element.isolateScope (). consulté) .toBe (vrai);););

Dans la section de test, un événement de clic est déclenché à l'aide de element.triggerHandler ("clic"). Lorsqu'un événement click est déclenché, la variable visualisée doit être définie sur vrai. Cette valeur est affirmée en utilisant expect (element.isolateScope (). consulté) .toBe (true).

9. Conclusion

Pour développer des projets Web modulaires et testables, AngularJS est le meilleur projet commun. Les directives sont l’un des meilleurs composants d’AngularJS. Cela signifie que plus vous en saurez sur les directives AngularJS, plus vous pourrez développer de projets modulables et testables.. 

Dans ce tutoriel, j'ai essayé de vous montrer les meilleures pratiques réelles en matière de directives et de garder à l'esprit que vous devez faire beaucoup de pratique pour comprendre la logique qui sous-tend les directives. J'espère que cet article vous aidera à bien comprendre les directives AngularJS.