web-dev-qa-db-fra.com

Lors de l'écriture d'une directive dans AngularJS, comment puis-je décider si je n'ai besoin d'aucun nouveau périmètre, d'un nouveau périmètre enfant ou d'un nouveau périmètre isolé?

Je suis à la recherche de directives que l’on peut utiliser pour déterminer le type de portée à utiliser lors de la rédaction d’une nouvelle directive. Idéalement, j'aimerais quelque chose de similaire à un organigramme qui me guide dans une série de questions et indique la réponse correcte - pas de nouvelle portée, nouvelle portée d'enfant ou nouvelle portée isolée - mais c'est probablement trop demander. Voici mes directives dérisoires actuelles:

Je suis conscient que l'utilisation d'une directive avec une portée isolée sur un élément oblige toutes les autres directives de ce même élément à utiliser le même (une) portée isolée, donc cela ne limite-t-il pas sérieusement l'utilisation d'une portée isolée?

J'espère que certains membres de l'équipe Angular-UI (ou d'autres ayant écrit de nombreuses directives) pourront partager leurs expériences.

N'ajoutez pas de réponse indiquant simplement "utilisez une portée isolée pour les composants réutilisables".

265
Mark Rajcok

Quelle belle question! J'adorerais ce que les autres ont à dire, mais voici les directives que j'utilise.

Le principe de haute altitude: scope est utilisé comme "colle" que nous utilisons pour communiquer entre le contrôleur parent, la directive et le modèle de directive.

Portée des parents:scope: false, donc pas de nouvelle portée du tout

Je ne l’utilise pas très souvent, mais comme @MarkRajcok l’a dit, si la directive n’accède à aucune variable de portée (et n’en définit évidemment aucune!), C’est très bien pour moi. Ceci est également utile pour les directives enfants qui sont uniquement utilisées dans le contexte de la directive parent (bien qu'il y ait toujours des exceptions à cela) et qui ne le font pas. avoir un modèle. Fondamentalement, tout ce qui a un modèle n'appartient pas au partage d'une portée, car vous exposez cette portée de manière inhérente pour l'accès et la 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 un graphique vectoriel (statique) à l'aide d'une bibliothèque SVG en cours d'écriture. Il $observes deux attributs (width et height) et les utilise dans ses calculs, mais il ne définit ni ne lit aucune variable de portée et n'a pas de modèle. C'est un bon cas d'utilisation pour ne pas créer une autre portée; nous n'en avons pas besoin, alors pourquoi s'en préoccuper?

Mais dans une autre directive SVG, cependant, je demandais un ensemble de données à utiliser et, en outre, je devais stocker un tout petit peu d’état. Dans ce cas, utiliser la portée parente serait irresponsable (encore une fois, généralement). Donc au lieu...

Child Scope:scope: true

Les directives avec une portée enfant sont sensibles au contexte et sont conçues pour interagir avec la portée actuelle.

De toute évidence, l'un des principaux avantages de cette solution par rapport à une portée isolée est que l'utilisateur est libre d'utiliser l'interpolation sur tous les attributs qu'il souhaite. par exemple. L'utilisation de class="item-type-{{item.type}}" sur une directive avec une étendue d'isolement ne fonctionnera pas par défaut, mais fonctionnera correctement sur une directive avec une étendue enfant, car tout ce qui est interpolé peut toujours être trouvé par défaut dans l'étendue parent. En outre, la directive elle-même peut évaluer en toute sécurité des attributs et des expressions dans le contexte de son propre domaine sans se soucier de la pollution ou des dommages subis par le parent.

Par exemple, une info-bulle est quelque chose qui vient d'être ajouté; une étendue d'isolement ne fonctionnerait pas (par défaut, voir ci-dessous), car nous nous attendons à utiliser d'autres directives ou attributs interpolés ici. L'info-bulle est simplement une amélioration. Mais l'info-bulle doit également définir certaines choses sur la portée à utiliser avec une sous-directive et/ou un modèle et évidemment gérer son propre état, il serait donc très déplorable d'utiliser la portée parent. Nous sommes en train de le polluer ou de l’endommager, et Bueno non plus.

Je me retrouve plus souvent à utiliser des portées enfants que des portées isolées ou parentales.

Isoler la portée:scope: {}

Ceci est pour les composants réutilisables. :-)

Mais sérieusement, je considère les "composants réutilisables" comme des "composants autonomes". L'intention est qu'elles soient utilisées dans un but spécifique. Par conséquent, leur combinaison avec d'autres directives ou l'ajout d'autres attributs interpolés au nœud DOM n'a naturellement aucun sens.

Pour être plus spécifique, tout ce qui est nécessaire pour cette fonctionnalité autonome est fourni via des attributs spécifiés évalués dans le contexte de la portée parente; ce sont des chaînes unidirectionnelles ('@'), des expressions unidirectionnelles ('&') ou des liaisons de variables bidirectionnelles ('=').

Sur les composants autonomes, il n’a aucun sens de devoir appliquer d’autres directives ou attributs sur celle-ci car elle existe par elle-même. Son style est régi par son propre modèle (si nécessaire) et peut faire transclure le contenu approprié (si nécessaire). Il est autonome, nous l'avons donc placé dans une portée isolée pour dire également: "Ne jouez pas avec cela. Je vous fournis une API définie à l'aide de ces quelques attributs."

Une bonne pratique consiste à exclure autant que possible les éléments basés sur des modèles des fonctions de liaison de directive et de contrôleur. Ceci fournit un autre point de configuration "semblable à une API": l'utilisateur de la directive peut simplement remplacer le modèle! Toutes les fonctionnalités sont restées les mêmes et son API interne n'a jamais été touchée, mais nous pouvons jouer avec le style et la mise en œuvre DOM autant que nécessaire. ui/bootstrap est un excellent exemple de la façon de bien le faire, car Peter & Pawel sont géniaux.

Les portées isolées sont également très utiles pour une transclusion. Prenez des onglets; ils ne sont pas seulement la fonctionnalité entière, mais tout ce qui est à l'intérieur peut être évalué librement à partir de la portée parente tout en laissant les onglets (et les volets) à faire ce qu'ils veulent. Les onglets ont clairement leur propre état , qui appartient à la portée (pour interagir avec le modèle), mais cet état n'a rien à voir avec le contexte dans il a été utilisé - il est entièrement interne à ce qui fait de la directive tab une directive tab. De plus, il n’a pas beaucoup de sens d’utiliser d’autres directives avec les onglets. Ce sont des onglets - et nous avons déjà cette fonctionnalité!

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

Cela étant dit, il convient de noter qu'il existe des moyens de contourner certaines des limitations (par exemple, les fonctionnalités) d'une portée isolée, comme l'a suggéré @ProLoser dans sa réponse. Par exemple, dans la section relative à la portée des enfants, j'ai mentionné l'interpolation sur la rupture d'attributs non directifs lors de l'utilisation d'une portée isolée (par défaut). Mais l'utilisateur pourrait par exemple simplement utiliser class="item-type-{{$parent.item.type}}" et cela fonctionnerait à nouveau. Par conséquent, s'il existe une raison impérieuse d'utiliser une portée isolée sur une portée enfant mais que certaines de ces limitations vous inquiètent, sachez que vous pouvez pratiquement toutes les contourner si vous en avez besoin.

Résumé

Les directives sans nouvelle portée sont en lecture seule; ils sont complètement dignes de confiance (c’est-à-dire internes à l’application) et ils ne touchent pas le jack. Les directives avec une portée enfant ajoutent des fonctionnalités , mais elles ne sont pas les seules Fonctionnalité. Enfin, les portées isolées concernent les directives qui constituent l’objectif entier; ils sont autonomes, donc c'est correct (et le plus "correct") de les laisser devenir voyous.

Je voulais faire part de mes idées initiales, mais comme je pense à plus de choses, je vais mettre à jour ceci. Mais merde sainte - c'est long pour une SO réponse ...


PS: Totalement tangentielle, mais puisque nous parlons d’objectifs, je préfère dire "prototypique" alors que d’autres préfèrent "prototypal", ce qui semble être plus précis mais ne fait pas très bien la différence. :-)

289
Josh David Miller

Ma politique personnelle et mon expérience:

Isolé: un bac à sable privé

Je souhaite créer un grand nombre de méthodes et de variables de portée qui sont UNIQUEMENT utilisées par ma directive et qui ne sont jamais vues ou directement consultées par l'utilisateur. Je souhaite ajouter à la liste blanche les données de périmètre dont je dispose. Je peux utiliser la transclusion pour permettre à l'utilisateur de revenir au champ parent (non affecté). Je PAS veux que mes variables et méthodes soient accessibles aux enfants non inclus.

Enfant: une sous-section de contenu

Je souhaite créer des méthodes et des variables de portée qui PEUVENT peuvent être consultées par l'utilisateur, mais ne concernent pas les portées environnantes (frères et sœurs et parents) en dehors du contexte de ma directive. Je voudrais aussi laisser TOUTES les données de la portée parente se répercuter de manière transparente.

Aucune: directives simples en lecture seule

Je n'ai pas vraiment besoin de jouer avec les méthodes ou les variables de la portée. Je fais probablement quelque chose qui n'a pas à voir avec les portées (telles que l'affichage de simples plugins jQuery, la validation, etc.).

Remarques

  • Vous ne devez pas laisser ngModel ou d’autres éléments influencer directement votre décision. Vous pouvez contourner un comportement étrange en faisant des choses comme ng-model=$parent.myVal (enfant) ou ngModel: '=' (isoler).
  • Isolate + transclude restaure tout le comportement normal des directives soeurs et revient à la portée parente, ne laissez donc pas cela affecter votre jugement.
  • Ne jouez pas avec la portée de aucune car c'est comme si vous mettiez des données dans la portée de la moitié inférieure du DOM mais pas de la moitié supérieure, ce qui est logique.
  • Faites attention aux priorités directives (vous n'avez pas d'exemples concrets de 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 d'étendue. Vous pouvez également utiliser require: '^ngModel' pour rechercher des éléments parents.
52
ProLoser

Après avoir écrit de nombreuses directives, j'ai décidé d'utiliser moins de isolated scope. Même si c'est cool et que vous encapsulez les données et veillez à ne pas les fuir dans l'étendue parent, cela limite considérablement le nombre de directives que vous pouvez utiliser ensemble. Alors,

Si la directive que vous allez écrire va se comporter entièrement toute seule et que vous n'allez pas la partager avec d'autres directives, optez pour portée isolée . (comme un composant, vous pouvez simplement le brancher, avec peu de personnalisation pour le développeur final) (cela devient très compliqué lorsque vous essayez d'écrire des sous-éléments contenant des directives)

Si la directive que vous allez écrire va , il suffit de faire des manipulations dom qui ne nécessitent aucun état interne de la portée, ni de modifications explicites de la portée (la plupart du temps très simples); aller pour pas de nouvelle portée . (comme ngShow, ngMouseHover, ngClick, ngRepeat)

Si la directive que vous allez écrire doit modifier certains éléments de la portée parent, mais doit également gérer un état interne, sélectionnez la nouvelle portée enfant . (comme ngController)

Assurez-vous de vérifier le code source pour les directives: https://github.com/angular/angular.js/tree/master/src/ng/directive
Cela aide beaucoup sur la façon de penser à eux

18
Umur Kontacı

Je pensais juste que j'ajouterais ma compréhension actuelle et sa relation avec d'autres concepts de SC.

Par défaut (par exemple, non déclarée ou étendue: false)

Ceci est philosophiquement équivalent à l’utilisation de variables globales. Votre directive peut accéder à tout ce qui se trouve dans le contrôleur parent, mais cela les affecte et s’affecte en même temps.

portée: {}

C'est comme un module, tout ce qu'il veut utiliser doit être explicitement passé. Si CHAQUE directive que vous utilisez est une portée d'isolement, cela peut être équivalent à ce que chaque fichier JS écrive son propre module avec beaucoup de surcharge en injectant toutes les dépendances.

portée: 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 vous étend simplement une copie de la portée parente. Si vous créez une portée isolée et transmettez chaque attribut et fonction de la portée parent, son fonctionnement est équivalent à celui-ci.


La clé est que N'IMPORTE QUELLE directive peut être écrite de n'importe quelle façon. Les différentes déclarations de portées ne sont là que pour vous aider à organiser. Vous pouvez tout transformer en module ou simplement utiliser toutes les variables globales et faire très attention. Pour faciliter la maintenance, il est toutefois 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. La raison pour laquelle ceci est délicat à mon sens, c'est que lorsque les gens apprennent ceci, ils pensent apprendre comment les directives fonctionnent, mais en fait, ils apprennent davantage sur l'organisation du code/logique.

Une autre chose qui m'a aidé à comprendre le fonctionnement des directives est d'apprendre à connaître ngInclude. ngInclude vous aide à inclure des partiels HTML. Lorsque j'ai commencé à utiliser les directives, j'ai découvert que vous pouviez utiliser son option de modèle pour réduire votre code, mais je n'attachais aucune logique.

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

9
user2483724

Je suis d'accord avec Umur. En théorie, les portées isolées semblent merveilleuses et "portables", mais en construisant mon application de manière à impliquer des fonctionnalités non triviales, je suis tombé sur la nécessité d'incorporer plusieurs directives (certaines imbriquées ou ajoutant des attributs) afin d'écrire propre HTML, qui est le but d'un langage spécifique au domaine.

En fin de compte, il est trop étrange de devoir transmettre chaque valeur globale ou partagée en aval de la chaîne avec plusieurs attributs à chaque invocation DOM d'une directive (comme l'exige la portée isolate). Il semble insensé d'écrire de manière répétée tout cela dans le DOM et cela semble inefficace, même s'il s'agit d'objets partagés. Cela complique également inutilement les déclarations de directive. La solution de contournement consistant à utiliser $ parent pour "atteindre" et récupérer des valeurs de la directive HTML semble être une très mauvaise forme.

Moi aussi, j'ai fini par changer mon application pour avoir principalement des directives de portée enfant avec très peu d'isolats - uniquement ceux qui n'ont pas besoin d'accéder à AUCUN élément du parent autre que ce qui peut être transmis via des attributs simples et non répétitifs.

Ayant rêvé pendant des décennies du rêve de langages spécifiques à un domaine, je suis ravi qu'AngularJS offre cette option et je sais que, à mesure que davantage de développeurs travaillent dans ce domaine, nous allons voir des applications très cool qui sont également faciles pour leurs architectes à écrire, développer et déboguer.

-- RÉ

2
Ungallery