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..
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 livre
. Cette directive retourne un objet, et parlons un peu de cet objet. restreindre
est 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ée
est 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. templateUrl
est 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èle
et 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 templateUrl
option.
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:
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:
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 lien
fonctionner 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. E
directive de type, etc..
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 C
, vous pouvez mettre à jour la directive restreindre
comme C
et l'utiliser comme suit:
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.
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.
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:
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 panier
partie 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ée
attribuer plus tard.
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.
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..
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:
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..
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 livre
. Cette directive prend l'argument Titre
et crée une div en utilisant ce titre. Dans le test, avant chaque section de test, nous appelons notre module masteringAngularJsDirectives
premier. 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.
Dans cette section, nous allons tester le champ d'application de la directive test de livre
. Cette 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 vu
sera défini comme vrai
. Dans notre test, nous vérifierons si vu
est 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)
.
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.