Lors de l’écriture d’une directive dans AngularJS, comment puis-je décider si je n’ai pas besoin d’une nouvelle étendue, d’une nouvelle étendue enfant ou d’une nouvelle étendue isolée?

Je cherche des lignes direcsortingces que l’on peut utiliser pour déterminer le type de scope à utiliser lors de la rédaction d’une nouvelle directive. Idéalement, je voudrais quelque chose de similaire à un organigramme qui me guide à travers un tas de questions et qui affiche la bonne réponse – pas de nouvelle scope, nouvelle scope enfant ou nouvelle scope d’isolat – mais cela demande probablement trop. Voici mon ensemble actuel de directives:

  • N’utilisez pas une étendue isolée si l’élément qui utilisera la directive utilise ng-model
    Voir Puis-je utiliser ng-model avec une scope isolée? et
    Pourquoi les formateurs ne fonctionnent pas avec une scope isolée?
  • Si la directive ne modifie aucune propriété de scope / modèle, ne créez pas de nouvelle étendue
  • Les scopes isolées semblent bien fonctionner si la directive encapsule un ensemble d’éléments DOM ( la documentation dit “une structure DOM complexe”) et la directive sera utilisée comme élément ou sans aucune autre directive sur le même élément.

Je suis conscient que l’utilisation d’une directive avec une étendue isolée sur un élément force toutes les autres directives sur ce même élément à utiliser la même (une) scope d’isolement.

J’espère que certains membres de l’équipe Angular-UI (ou d’autres qui ont écrit de nombreuses directives) peuvent partager leurs expériences.

S’il vous plaît ne pas append une réponse qui dit simplement “utiliser une scope isolée pour les composants réutilisables”.

Quelle bonne question! J’aimerais entendre ce que les autres ont à dire, mais voici les lignes direcsortingces que j’utilise.

Le principe de haute altitude: la scope est utilisée comme “colle” pour communiquer entre le contrôleur parent, la directive et le modèle de directive.

Portée parent: scope: false , donc pas de nouvelle scope

Je ne l’utilise pas très souvent, mais comme l’a dit @MarkRajcok, si la directive n’accède à aucune variable de scope (et n’en définit évidemment aucune!), Cela me convient parfaitement. Ceci est également utile pour les directives enfants qui ne sont utilisées que dans le contexte de la directive parente (bien qu’il y ait toujours des exceptions à cela) et qui n’ont pas de modèle. Fondamentalement, tout élément d’un modèle n’appartient pas au partage d’une étendue, car vous exposez de manière inhérente cette scope à des fins d’access et de manipulation (mais je suis sûr qu’il existe des exceptions à cette règle).

Par exemple, j’ai récemment créé une directive qui dessine une image vectorielle (statique) en utilisant une bibliothèque SVG que je suis en train d’écrire. Il $observe deux atsortingbuts ( width et height ) et les utilise dans ses calculs, mais il ne définit ni ne lit aucune variable de scope et n’a aucun modèle. Ceci est un cas utile pour ne pas créer une autre scope; nous n’en avons pas besoin, alors pourquoi s’en préoccuper?

Mais dans une autre directive SVG, cependant, j’avais besoin d’un dataset à utiliser et je devais également stocker un tout petit peu d’état. Dans ce cas, l’utilisation de la scope parent serait irresponsable (encore une fois, de manière générale). Donc au lieu…

Portée de l’enfant: scope: true

Les directives avec une scope enfant sont adaptées au contexte et sont destinées à interagir avec la scope actuelle.

De toute évidence, un avantage clé de ceci sur une scope d’isolat est que l’utilisateur est libre d’utiliser l’interpolation pour tous les atsortingbuts qu’il souhaite. ex: using class="item-type-{{item.type}}" sur une directive avec une scope d’isolat ne fonctionnera pas par défaut, mais fonctionne bien sur une étendue d’enfant car tout ce qui est interpolé peut toujours être trouvé dans la scope parent. En outre, la directive elle-même peut évaluer en toute sécurité les atsortingbuts et les expressions dans le contexte de son propre champ d’action sans se soucier de la pollution ou des dommages subis par le parent.

Par exemple, une info-bulle est simplement ajoutée. une scope d’isolat ne fonctionnerait pas (par défaut, voir ci-dessous) car il est prévu d’utiliser d’autres directives ou atsortingbuts interpolés ici. L’info-bulle n’est qu’une amélioration. Mais l’info-bulle doit également définir certaines choses sur le périmètre à utiliser avec une sous-directive et / ou un modèle et évidemment pour gérer son propre état, il serait donc très difficile d’utiliser la scope parent. Nous le polluons ou l’endommage, et le bueno non plus.

Je me retrouve à utiliser des scopes enfants plus souvent que des scopes isolées ou parentes.

Isoler la scope: scope: {}

Ceci est pour les composants réutilisables. 🙂

Mais sérieusement, je pense aux “composants réutilisables” en tant que “composants autonomes”. L’intention est qu’ils doivent être utilisés dans un but spécifique. Par conséquent, les combiner avec d’autres directives ou append d’autres atsortingbuts interpolés au noeud DOM n’a pas de sens.

Pour être plus précis, tout ce qui est nécessaire pour cette fonctionnalité autonome est fourni via des atsortingbuts spécifiés évalués dans le contexte de la scope parente; ce sont soit des chaînes à sens unique (‘@’), des expressions à sens unique (‘&’), soit des liaisons de variables à deux voies (‘=’).

Sur les composants autonomes, il est inutile d’appliquer d’autres directives ou atsortingbuts car il existe par lui-même. Son style est régi par son propre modèle (si nécessaire) et peut contenir le contenu approprié (si nécessaire). C’est autonome, nous l’avons donc placé dans un cadre isolé pour dire: “Ne vous mêlez pas de cela. Je vous donne une API définie via ces quelques atsortingbuts.”

Une bonne pratique consiste à exclure autant que possible les fonctions basées sur des modèles du lien de directive et des fonctions du contrôleur. Cela fournit un autre sharepoint configuration “API-like”: l’utilisateur de la directive peut simplement remplacer le modèle! Les fonctionnalités sont toutes restées les mêmes, et son API interne n’a jamais été touchée, mais nous pouvons nous soucier du style et de l’implémentation DOM autant que nécessaire. ui / bootstrap est un excellent exemple de la façon de faire ça parce que Peter & Pawel sont géniaux.

Les scopes isolées sont également idéales pour la transclusion. Prendre des tabs; ils ne sont pas seulement la fonctionnalité complète, mais tout ce qui est à l’ intérieur peut être évalué librement à partir de la scope parent tout en laissant les tabs (et les volets) faire ce qu’ils veulent. Les tabs ont clairement leur propre état , qui appartient à la scope (pour interagir avec le modèle), mais cet état n’a rien à voir avec le contexte dans lequel il a été utilisé – il est entièrement interne à une directive tabulation. De plus, cela n’a pas beaucoup de sens d’utiliser d’autres directives avec les tabs. Ce sont des tabs – et nous avons déjà cette fonctionnalité!

Entourez-le avec plus de fonctionnalités ou transférez davantage de fonctionnalités, mais la directive est ce qu’elle est déjà.

Cela dit, je dois noter qu’il existe des moyens de contourner certaines des limitations (c.-à-d. Les caractéristiques) d’une scope isolée, comme l’a suggéré @ProLoser dans sa réponse. Par exemple, dans la section de la scope enfant, j’ai mentionné l’interpolation sur les atsortingbuts non directives qui se brisent lors de l’utilisation d’une scope d’isolement (par défaut). Mais l’utilisateur pourrait, par exemple, simplement utiliser class="item-type-{{$parent.item.type}}" et cela fonctionnerait à nouveau. Donc, s’il existe une raison impérieuse d’utiliser une scope d’isolement sur une scope enfant, mais que vous craignez certaines de ces limitations, sachez que vous pouvez les contourner pratiquement tous, si nécessaire.

Résumé

Les directives sans nouvelle scope sont en lecture seule; ils sont complètement fiables (c.-à-d. interne à l’application) et ils ne touchent pas jack. Les directives avec une scope enfant ajoutent des fonctionnalités, mais elles ne sont pas la seule . Enfin, les champs d’isolement sont destinés aux directives qui constituent l’objective entier; ils sont autonomes, alors c’est bien (et le plus “correct”) de les laisser devenir des voyous.

Je voulais sortir mes premières idées, mais comme je pense à plus de choses, je mettrai à jour cela. Mais la merde sacrée – c’est long pour une réponse SO …


PS: Totalement tangentiel, mais puisque nous parlons de scopes, je préfère dire “prototypique”, alors que d’autres préfèrent “prototypal”, qui semble plus précis, mais qui ne fonctionne pas bien du tout. 🙂

Ma politique personnelle et mon expérience:

Isolé: un bac à sable privé

Je veux créer beaucoup de méthodes et de variables de scope qui sont UNIQUEMENT utilisées par ma directive et qui ne sont jamais vues ou directement consultées par l’utilisateur. Je souhaite mettre en liste blanche les données de scope disponibles. Je peux utiliser la transclusion pour permettre à l’utilisateur de revenir à la scope parent (non affectée) . Je ne souhaite PAS que mes variables et méthodes soient accessibles aux enfants exclus.

Enfant: une sous-section de contenu

Je souhaite créer des méthodes et des variables de scope auxquelles l’utilisateur peut accéder, mais qui ne concernent pas les étendues environnantes (frères et soeurs et parents) en dehors du contexte de ma directive. J’aimerais aussi que TOUTES les données relatives à la scope parentale soient diffusées de manière transparente.

Aucun: directives simples, en lecture seule

Je n’ai pas vraiment besoin de jouer avec des méthodes ou des variables de scope. Je fais probablement quelque chose qui n’a rien à voir avec les étendues (comme l’affichage de plugins jQuery simples, la validation, etc.).

Remarques

  • Vous ne devez pas laisser ngModel ou d’autres choses influencer directement votre décision. Vous pouvez contourner les comportements bizarres en faisant des choses comme ng-model=$parent.myVal (enfant) ou ngModel: '=' (isoler).
  • Isolate + transclude restaurera tous les comportements normaux en directives fraternelles et retournera à la scope parente, ne laissez pas cela non plus affecter votre jugement.
  • Ne vous moquez pas de la scope sur aucun, car c’est comme mettre des données sur la scope pour la moitié inférieure du DOM, mais pas sur la moitié supérieure, ce qui n’a aucun sens.
  • Faites attention aux priorités de la directive (ne pas avoir d’exemples concrets sur la façon dont cela peut affecter les choses)
  • Injectez des services ou utilisez des contrôleurs pour communiquer entre les directives avec n’importe quel type de scope. Vous pouvez également require: '^ngModel' pour rechercher des éléments parents.

Après avoir écrit beaucoup de directives, j’ai décidé d’utiliser une scope moins isolated . Même si c’est cool et que vous encapsulez les données et que vous veillez à ne pas divulguer les données à la scope parente, cela limite considérablement la quantité de directives que vous pouvez utiliser ensemble. Alors,

Si la directive que vous allez écrire se comportera entièrement de manière autonome et que vous ne la partagerez pas avec d’autres directives, optez pour une scope isolée . (comme un composant que vous pouvez simplement twigr, avec peu de personnalisation pour le développeur final) (cela devient plus compliqué lorsque vous essayez d’écrire des sous-éléments contenant des directives)

Si la directive que vous allez écrire ne fera que des manipulations dom, qui ne requièrent aucun état interne de scope, ou des modifications explicites de la scope (surtout des choses très simples); aller pour aucune nouvelle scope . (comme ngShow , ngMouseHover , ngClick , ngRepeat )

Si la directive que vous allez écrire doit modifier certains éléments de la scope parent, mais doit également gérer certains états internes, optez pour la nouvelle étendue enfant . (comme ngController )

Assurez-vous de vérifier le code source des directives: https://github.com/angular/angular.js/tree/master/src/ng/directive
Cela aide grandement à penser à eux

Je pensais juste append ma compréhension actuelle et comment elle se rapporte aux autres concepts de JS.

Valeur par défaut (par exemple, non déclarée ou scope: false)

Ceci est philosophiquement équivalent à l’utilisation de variables globales. Votre directive peut accéder à tout dans le contrôleur parent mais elle les affecte également et est affectée en même temps.

scope:{}

C’est comme un module, tout ce qu’il veut utiliser doit être transmis explicitement. Si TOUTES les directives que vous utilisez sont une scope d’isolement, cela peut être l’équivalent de faire un fichier EVERY JS, vous écrivez son propre module avec beaucoup de surcharge lors de l’injection de toutes les dépendances.

scope: enfant

C’est le juste milieu entre les variables globales et le relais explicite. Il est similaire à la chaîne de prototypes de javascript et ne fait que prolonger la copie de la scope parente. Si vous créez une étendue d’isolement et que vous transmettez tous les atsortingbuts et fonctions de la scope parente, cela est équivalent sur le plan fonctionnel.


La clé est que toute directive peut être écrite de n’importe quelle manière. Les différentes déclarations de scope sont juste là pour vous aider à organiser. Vous pouvez tout faire comme un module, ou vous pouvez simplement utiliser toutes les variables globales et faire très attention. Pour faciliter l’entretien, il est préférable de modulariser votre logique en parties logiquement cohérentes. Il existe un équilibre entre une prairie ouverte et une prison fermée. Je pense que la raison en est que lorsque les gens apprennent à ce sujet, ils pensent qu’ils apprennent comment fonctionnent les directives, mais en fait ils apprennent à organiser le code / la logique.

Une autre chose qui m’a aidé à comprendre comment fonctionnent les directives est d’apprendre sur ngInclude. ngInclude vous aide à inclure les partiels HTML. Lorsque j’ai commencé à utiliser les directives, j’ai découvert que vous pouviez utiliser cette option pour réduire votre code, mais je n’attachais pas vraiment de logique.

Bien sûr, entre les directives d’angular et le travail de l’équipe d’ angular-ui , je n’ai pas encore eu à créer ma propre directive qui fait quelque chose de substantiel, donc mon sharepoint vue peut être complètement faux.

Je suis d’accord avec Umur. En théorie, les scopes isolées sont merveilleuses et “portables”, mais en créant mon application pour implémenter des fonctionnalités non sortingviales, j’ai eu besoin d’incorporer plusieurs directives (certaines nestedes dans d’autres ou leur ajoutant des atsortingbuts). propre HTML, qui est le but d’un langage spécifique au domaine.

En fin de compte, il est trop étrange de devoir passer chaque valeur globale ou partagée en aval de la chaîne avec plusieurs atsortingbuts sur chaque appel DOM d’une directive (comme cela est requirejs avec la scope d’isoler). Il semble juste idiot de ré-écrire tout cela dans le DOM et cela semble inefficace, même si ce sont des objects partagés. Cela complique aussi inutilement les déclarations de directives. La solution de rechange consistant à utiliser $ parent pour “atteindre” et récupérer des valeurs de la directive HTML semble être Very Bad Form.

Moi aussi, j’ai fini par changer mon application pour avoir principalement des directives de scope enfant avec très peu d’isolats – seulement celles qui n’ont pas besoin d’accéder à rien du parent autre que ce qu’elles peuvent être passées à travers des atsortingbuts simples et non répétitifs.

Ayant rêvé le rêve des langages spécifiques à un domaine pendant des décennies avant qu’il n’y ait une telle chose, je suis ravi que AngularJS propose cette option et je sais que plus les développeurs travailleront dans ce domaine, plus nous verrons des applications très intéressantes. sont également faciles pour leurs architectes à écrire, développer et déboguer.

— RÉ