Maison > Article > interface Web > Comment créer une directive personnalisée dans AngularJS ? Processus détaillé de création d'instructions personnalisées avec angulairejs
Documentation traduite en angularjs.org La documentation explique quand vous souhaitez créer vos propres directives dans une application AngularJS et comment les implémenter. Regardons ensemble cet article
À un niveau élevé, les directives sont du balisage sur un élément DOM (en tant qu'attributs, noms d'éléments, annotations et classes CSS) utilisé pour indiquer au compilateur HTML d'Angularjs ($compile) d'attacher un comportement spécifique à cet élément DOM. (par exemple via l'écoute d'événements), ou encore pour transformer un élément DOM et ses enfants.
Angularjs est livré avec un ensemble d'implémentations intégrées, comme ngBind, ngModel et ngClass. En plus des contrôleurs et des services que vous créez, vous pouvez créer vos propres directives à utiliser au démarrage d'Angularjs (. bootstraps) de votre application, le compilateur HTML parcourt les directives de correspondance DOM correspondant aux éléments DOM.
Que signifie « compiler » un modèle HTML ? Pour AngularJS, « compilation » signifie attacher des directives au HTML pour le rendre interactif. La raison pour laquelle nous utilisons le terme « compiler » est que le. le processus récursif d'attachement de directives reflète le processus de compilation du code source dans les langages de programmation compilés
correspondance de directives
Avant de commencer à écrire des directives, nous devons savoir lors de l'utilisation d'une directive donnée, comment le HTML. Le compilateur détermine
Semblable à la terminologie utilisée lorsqu'un élément correspond à un sélecteur, nous disons qu'un élément correspond à une directive lorsque la directive fait partie de sa déclaration.
Dans l'exemple ci-dessous, nous disons que l'élément <input> correspond à la directive ngModel
<input> <!-- as an attr -->
Les éléments <input> suivants correspondent également à ngModel :
<input>
Les éléments
<person>{{name}}</person>
AngularJS normalise les noms de balises et d'attributs des éléments pour déterminer quels éléments correspondent quelles directives correspondent. Nous définissons généralement (nous référons à) une directive par son nom canonique camelCase, sensible à la casse (par exemple, ngModel). Cependant, comme HTML n'est pas sensible à la casse, nous faisons référence aux instructions dans le DOM en minuscules, en utilisant généralement le délimiteur tiret (-) pour séparer les différents mots (tels que ng-model)
Le processus de normalisation est le suivant :
1. Supprimez x-, data- au début de l'élément/attribut ;
2. Convertissez les délimiteurs -, _, en camelCased camelCas
Par exemple, les formes suivantes sont toutes équivalentes et sont les identique à la correspondance des directives ngBind :
Hello <input>
A - attributes <p> </p> C - class name <p> </p> E - element name <person></person> M - comments <!-- directive: person -->
Meilleure pratique : préférez utiliser les directives via le nom de la balise et les attributs plutôt que les noms de commentaires et de classes. Cela rend généralement il est plus facile de déterminer à quelles directives correspond un élément donné.
Meilleure pratique : les directives de commentaire étaient couramment utilisées dans les endroits où l'API DOM limite la possibilité de créer des directives couvrant plusieurs éléments (par exemple à l'intérieur elements). AngularJS 1.2 introduit ng-repeat-start et ng-repeat-end comme meilleure solution à ce problème..
Parlons d'abord de l'API pour l'enregistrement des directives. Comme les contrôleurs, les directives sont également enregistrées sur les modules. Pour enregistrer une directive, vous devez utiliser l'API module.directive. module.directive accepte un nom de directive standardisé, suivi d'une fonction d'usine. Cette fonction d'usine doit renvoyer un objet avec différentes options pour indiquer à la directive $compile comment elle doit se comporter lorsqu'elle est mise en correspondance.
La fonction factory n'est appelée qu'une seule fois lorsque $conpile correspond pour la première fois à la directive. Vous pouvez charger n'importe quel travail d'initialisation ici. Cette fonction (d'usine) est appelée à l'aide de $injector.invoke, ce qui la rend injectable comme un contrôleur.
Nous passerons en revue quelques exemples de directives courants, puis nous plongerons dans les différentes options et le processus de compilation.
Bonne pratique : afin d'éviter les collisions avec une future norme, il est préférable de préfixer vos propres noms de directive. Par exemple, si vous créez une directive
在上面的例子中,我们列出了模板选项(template attribute of return object in factory function),但随着模板大小的增长,这将变得令人讨厌。
Best Practice: Unless your template is very small, it's typically better to break it apart into its own HTML file and load it with the templateUrl option.
如果你熟悉ngInclude,templateUrl就像它一样工作。下面是使用templateUrl代替的相同示例:
templateUrl也可以是一个函数,它返回要加载和用于指令的HTML模板的URL。AngularJS将使用两个参数调用templateUrl函数:指令被调用的元素以及与该元素相关联的attr对象。
Note: You do not currently have the ability to access scope variables from the templateUrl function, since the template is requested before the scope is initialized
注:(要访问socpe上的值,应该在post-link阶段).
When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template.The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.
用元素来使用myCustomer指令时明智的选择,因为你不用一些“customer”行为修饰一个元素,你定义一个元素核心行为作为一个costomer组建。(想看更多就到PHP中文网angularjs参考手册中学习)
我们以上的myCustomer指令很好,但是它有一个致命缺陷。我们只有在一个给定的scope下使用。
在其目前的实现上,我们应该需要去创建一些不同点控制器用来重用这个指令。
https://plnkr.co/edit/CKEgb1e...
这明显不是一个好的解决方案。
我们说项的是把指令内部的scope与外部scope(controller scope)分离,并且映射外部scope到指令内部scope。我们可以通过创建一个isolate scope来做。为此,我们可以使用指令的scope选项。
https://plnkr.co/edit/E6dTrgm...
看index.html文件,第一个
让我们仔细看看scope选项
//... scope: { customerInfo: '=info' }, //...
除了可以将不同的数据绑定到指令中的作用域外,使用isolated scope还有其他作用。
我们可以通过添加另一个属性vojta来展示,到我们的scope并尝试从我们的指令模板中访问它:
https://plnkr.co/edit/xLVqnzt...
请注意{{vojta.name}}和{{vojta.address}}为空,意味着它们未定义(undefined)。虽然我们在控制器中定义了vojta,但它在指令中不可用。
顾名思义,该指令的 isolate scope隔离了除显式添加到作用域的模型之外的所有内容:scope: {}散列对象. 这在构建可重用组件时很有用,因为它可以防止组件改变模型状态,除了显式传入。
Note: Normally, a scope prototypically inherits from its parent. An isolated scope does not. See the "Directive Definition Object - scope"section for more information about isolate scopes.
Best Practice: Use the scope option to create isolate scopes when making components that you want to reuse throughout your app.
在这个例子中,我们将建立一个显示当前时间的指令。每秒一次,它会更新DOM以反映当前时间。
想要修改DOM的指令通常使用link选项来注册DOM监听器以及更新DOM。它在模板被克隆之后执行,并且是放置指令逻辑的地方。
link接受一个带有一下签名的函数function link(scope, element, attrs, controller, transcludeFn) { ... }, 其中:
scope是一个Angularjs scope 对象
element 是一个此指令匹配的jqLite包装元素
attrs是一个具有标准化属性名称及其对应属性值的键值对的散列对象。
controller是指令所需的控制器实例或其自己的控制器(如果有的话)。确切的值取决于指令的 require属性。
transcludeFn是预先绑定到正确的包含范围的transclude链接函数。
For more details on the link option refer to the $compile API page.
Dans notre fonction de lien, nous souhaitons mettre à jour l'heure affichée toutes les secondes, ou un utilisateur modifie la chaîne de format d'heure liée à notre commande. Nous utiliserons le service $interval pour appeler périodiquement le gestionnaire. C'est plus facile que d'utiliser $timeout, mais c'est également mieux pour les tests de bout en bout, où nous voulons nous assurer que tous les $timeouts sont terminés avant de terminer le test. Si la directive est supprimée, nous souhaitons également supprimer $interval, afin de ne pas introduire de fuite de mémoire
https://plnkr.co/edit/vIhhmNp...
Il y en a quelques-uns choses à noter ici. Tout comme l'API module.controller, les paramètres de fonction dans module.directive sont des dépendances injectées. Nous pouvons donc utiliser $interval et dateFilter dans la fonction de lien de la directive.
Nous enregistrons un événement element.on('$destroy', ...). Qu'est-ce qui déclenche cet événement $destroy ?
AngularJS publie quelques événements spéciaux. Lorsqu'un nœud DOM compilé avec le compilateur AngularJS est détruit, il déclenche l'événement $ destroy. De même, lorsqu'une portée Angularjs est détruite, elle diffuse un événement $ destroy aux portées d'écoute.
En écoutant cet événement, les écouteurs d'événements susceptibles de provoquer des fuites de mémoire peuvent être supprimés. Les événements d'écoute enregistrés dans la portée et l'élément seront automatiquement nettoyés lorsque le DOM est détruit, mais si vous avez enregistré un écouteur sur le service ou enregistré un écouteur sur un nœud DOM qui n'a pas été supprimé, vous devez le nettoyer vous-même, sinon Vous risquez d'introduire une fuite de mémoire.
Bonne pratique : les directives doivent être nettoyées après elles-mêmes. Vous pouvez utiliser element.on('$destroy', ...) ou scope.$on('$destroy', ...) pour exécuter une fonction de nettoyage lorsque la directive est supprimée. Mais parfois, vous souhaitez pouvoir transmettre un modèle entier au lieu d'une chaîne ou d'un objet. Nous disons que nous voulons créer un composant « boîte de dialogue ». La boîte de dialogue doit avoir la capacité d'envelopper tout contenu arbitraire.
Pour cela, nous devons utiliser l'option transclure.À quoi sert exactement l'option de transclusion ? transclude rend le contenu de la directive accessible via cette option à la portée de la directive externe plutôt qu'à la portée interne.
Pour illustrer cela, regardez l'exemple ci-dessous. Notez que nous avons ajouté une fonction de lien dans script.js, redéfinissant le nom en Jeff. Selon vous, que permettra la liaison {{name}} ?
Comme d'habitude, nous avons pensé que {{name}} devrait être Jeff. Mais ce que nous voyons, c'est Tobias.
L'option transclure modifie la façon dont les étendues sont imbriquées. Cela fait que le contenu d'une directive transcluse a n'importe quel contenu de portée en dehors de la directive, plutôt que des portées internes. Ce faisant, il rend le contenu accessible au périmètre externe.
Meilleure pratique : utilisez uniquement transclude: true lorsque vous souhaitez créer une directive qui encapsule du contenu arbitraire.
Ensuite, nous souhaitons ajouter des boutons à cette boîte de dialogue, et permet aux utilisateurs utilisant la directive de lier leur propre comportement à la boîte de dialogue.Nous voulons exécuter la fonction que nous transmettons en l'appelant depuis la portée de la directive, mais elle s'exécutera dans le contexte de la portée enregistrée.
Nous avons déjà vu comment utiliser =attr dans l'option scope, mais dans l'exemple ci-dessus, nous avons utilisé &attr à la place. La liaison & permet à une directive de déclencher l'évaluation d'une expression dans une portée primitive à un moment précis. Toute expression légale est autorisée, y compris celle contenant un appel de fonction. En tant que tel, & contraignant est idéal pour lier des fonctions de rappel à des actions de directive.
Meilleure pratique : utilisez &attr dans l'option scope lorsque vous souhaitez que votre directive expose une API pour la liaison aux comportements. .
Créer une directive qui ajoute un écouteur d'événementAuparavant, nous utilisions des fonctions de lien pour créer des directives qui manipulent leurs éléments DOM. En nous basant sur cet exemple, créons une directive qui réagit aux événements sur son élément.
Par exemple, que se passe-t-il si nous voulons créer une directive qui permet à l'utilisateur de faire glisser un élément ?你可以组建任何指令通过模板使用他们。
有时,你需要一个由指令组合构建的组件。
想象你想要有一个容器,其中容器的内容对应于哪个选项卡处于活动状态的选项卡。
myPane指令有require选项值为^^myTabs. 当指令使用此选项,&compile将抛出一个错误除非特定的controller被找到。 ^^前缀表示该指令在其父元素上搜索控制器。(^前缀将使指令在自身元素或她的父元素上寻找控制器;又没任何前缀,指令将值操作自身)
所以这个myTabs contoller从哪里来的?指令可以特定一个controllers通过使用 controller选项。如你所见,myTabs指令使用了此选项。就像ngController,此选项附加一个控制器到指令的模板上。
如果需要从模板中引用控制器或绑定到控制器的任何功能,则可以使用选项controllerAs将控制器的名称指定为别名。该指令需要定义要使用的此配置的范围。这在指令被用作组件的情况下特别有用。
回头看myPane的定义,注意到link函数的最后一个参数:tabCtrl。当指令需要控制器时,它将接收该控制器作为其link函数的第四个参数。利用这一点,myPane可以调用myTabs的addPane函数。
如果需要多个控制器,则指令的require选项可以采用数组参数。发送给链接函数的相应参数也将是一个数组。
angular.module('docsTabsExample', []) .directive('myPane', function() { return { require: ['^^myTabs', 'ngModel'], restrict: 'E', transclude: true, scope: { title: '@' }, link: function(scope, element, attrs, controllers) { var tabsCtrl = controllers[0], modelCtrl = controllers[1]; tabsCtrl.addPane(scope); }, templateUrl: 'my-pane.html' }; });
明的读者可能想知道链接和控制器之间的区别。基本的区别是控制器可以暴露一个API,并且链接函数可以使用require与控制器交互。
Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.
到此我们已经看了大多数指令的用法,每一个样例演示了一个创建你自己指令的好的起始点。
你可能深入感兴趣于编译过程的解释可以在这里获得compiler guide.
$compile API 有一个全面的指令清单选项以供参考。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!