Je suis à la recherche de lignes directrices que l'on peut utiliser pour aider à déterminer quel type de portée à utiliser lors de l'écriture d'une nouvelle directive. Idéalement, j'aimerais quelque chose de similaire à un organigramme qui me guidera à travers un tas de questions et sort la bonne réponse - pas de nouvelle portée, de nouvelle portée enfant ou de nouvelle portée isolée - mais cela demande probablement trop. Voici mon ensemble dérisoire actuel de directives:
- N'utilisez pas une portée isolée si l'élément qui utilisera la directive utilise ng-model
Voir Puis-je utiliser ng-model avec une portée isolée? et
Pourquoi les formateurs ne fonctionnent pas avec une portée isolée? - Si la directive ne modifie aucune propriété de portée / modèle, ne créez pas de nouvelle portée
- Les portées 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 autres directives sur le même élément.
Je suis conscient que l'utilisation d'une directive avec une portée isolée sur un élément force toutes les autres directives sur ce même élément à utiliser la même (une) portée isolée, donc cela ne limite-t-il pas sérieusement quand une portée isolée peut être utilisée?
J'espère que certains membres de l'équipe Angular-UI (ou d'autres qui ont écrit de nombreuses directives) pourront partager leurs expériences.
Veuillez ne pas ajouter de réponse qui dit simplement "utiliser une portée isolée pour les composants réutilisables".
la source
scope: true
créera une portée enfant en utilisant$scope.new()
automatiquement.scope: false
(la valeur par défaut, pas de nouvelle portée),scope: true
(nouvelle portée qui hérite du prototype) etscope: { ... }
(nouvelle portée isolée).Réponses:
Quelle bonne question! Je l' aime entendre ce que les autres ont à dire, mais voici les directives que j'utilise.
La prémisse à haute altitude: la portée est utilisée comme la «colle» que nous utilisons pour communiquer entre le contrôleur parent, la directive et le modèle de directive.
Portée parent:,
scope: false
donc pas de nouvelle portée du toutJe n'utilise pas cela très souvent, mais comme l'a dit @MarkRajcok, si la directive n'accède à aucune variable de portée (et évidemment n'en définit aucune!), Cela ne pose aucun problème en ce qui me concerne. Cela est également utile pour les directives enfants qui ne sont utilisées que dans le contexte de la directive parent (bien qu'il y ait toujours des exceptions à cela) et qui n'ont pas de modèle. Fondamentalement, tout ce qui a un modèle n'appartient pas à partager une étendue, car vous exposez intrinsèquement cette étendue pour l'accès et la manipulation (mais je suis sûr qu'il y a des exceptions à cette règle).
À titre d'exemple, j'ai récemment créé une directive qui dessine un graphique vectoriel (statique) à l'aide d'une bibliothèque SVG que je suis en train d'écrire. Il s'agit de
$observe
deux attributs (width
etheight
) et les utilise dans ses calculs, mais il ne définit ni ne lit aucune variable de portée et n'a aucun modèle. Il s'agit d'un bon cas d'utilisation pour ne pas créer une autre étendue; nous n'en avons pas besoin, alors pourquoi s'embêter?Mais dans une autre directive SVG, cependant, j'avais besoin d'un ensemble de données à utiliser et je devais en outre stocker un tout petit peu d'état. Dans ce cas, l'utilisation de la portée parent serait irresponsable (encore une fois, d'une manière générale). Donc au lieu...
Portée enfant:
scope: true
Les directives avec une portée enfant sont contextuelles et sont destinées à interagir avec la portée actuelle.
De toute évidence, un avantage clé de cela sur 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
class="item-type-{{item.type}}"
d'une directive avec une portée isolée ne fonctionnera pas par défaut, mais fonctionne très bien avec une directive enfant car tout ce qui est interpolé peut toujours être trouvé par défaut dans la portée parent. De plus, la directive elle-même peut évaluer en toute sécurité les attributs et les expressions dans le contexte de sa propre portée sans se soucier de la pollution ou des dommages au parent.Par exemple, une info-bulle est quelque chose qui vient d'être ajoutée; une portée isolée ne fonctionnerait pas (par défaut, voir ci-dessous) car il est prévu que nous utiliserons ici d'autres directives ou attributs interpolés. L'info-bulle n'est qu'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 assez mauvais d'utiliser la portée parent. Nous le polluons ou l'endommagons, et ni l'un ni l'autre n'est bueno.
Je me retrouve à utiliser des portées enfant plus souvent que des portées isolées ou parent.
Isoler la portée:
scope: {}
C'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 doivent être utilisées dans un but spécifique, donc les combiner avec d'autres directives ou ajouter d'autres attributs interpolés au nœud DOM n'a pas de sens en soi.
Pour être plus précis, 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 parent; ce sont soit des chaînes unidirectionnelles ('@'), des expressions unidirectionnelles ('&'), soit des liaisons de variables bidirectionnelles ('=').
Sur les composants autonomes, il n'est pas logique de devoir lui appliquer d'autres directives ou attributs 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). Il est autonome, nous l'avons donc mis dans une portée isolée pour dire: "Ne jouez pas avec ça. Je vous donne une API définie à travers ces quelques attributs."
Une bonne pratique consiste à exclure autant de choses basées sur des modèles du lien directif et des fonctions de contrôleur que possible. Cela fournit un autre point de configuration "de type API": l'utilisateur de la directive peut simplement remplacer le modèle! La fonctionnalité est restée la même 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 nous le devons. ui / bootstrap est un excellent exemple de la façon de bien faire cela parce que Peter & Pawel sont géniaux.
Les portées isolées sont également idéales pour une utilisation avec la transclusion. Prenez des onglets; ils ne sont pas seulement la fonctionnalité tout, mais tout ce qui est à l' intérieur de celui - ci peuvent être évalués librement dans le cadre des parents tout en laissant les pattes (et vitres) pour 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 lequel il a été utilisé - il est entièrement interne à ce qui fait d'une directive tab une directive tab. De plus, cela 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 plus de fonctionnalités ou transcluez plus 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'est-à-dire les fonctionnalités) d'une portée isolée, comme l'a suggéré @ProLoser dans sa réponse. Par exemple, dans la section portée enfant, j'ai mentionné l'interpolation sur les attributs non directifs se brisant 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. Donc, s'il existe une raison impérieuse d'utiliser une étendue isolée sur une étendue enfant mais que vous vous inquiétez de certaines de ces limitations, sachez que vous pouvez les contourner presque toutes si vous en avez besoin.Résumé
Les directives sans nouvelle portée sont en lecture seule; ils sont totalement fiables (c'est-à-dire internes à l'application) et ils ne touchent pas à la prise. Les directives avec une portée enfant ajoutent des fonctionnalités, mais ce ne sont pas les seules fonctionnalités. Enfin, les étendues d'isolement sont destinées aux directives qui sont le but entier; ils sont autonomes, il est donc correct (et le plus "correct") de les laisser devenir voyous.
Je voulais faire part de mes premières réflexions, mais en pensant à plus de choses, je mettrai à jour cela. Mais merde - c'est long pour une réponse SO ...
PS: Totalement tangentiel, mais comme nous parlons de portées, je préfère dire "prototypique" alors que d'autres préfèrent "prototypique", qui semble être plus précis mais qui ne déroule pas du tout bien de la langue. :-)
la source
ngInclude
. Ou faites-le dans le cadre de votre build. De nombreuses options!Ma politique personnelle et mon expérience:
Isolé: un bac à sable privé
Je veux 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 ni directement accessibles par l'utilisateur. Je souhaite mettre en liste blanche les données de portée dont je dispose. Je peux utiliser la transclusion pour permettre à l'utilisateur de revenir à la portée parent (non affecté) . Je ne veux PAS que mes variables et méthodes soient accessibles aux enfants transclus.
Enfant: une sous-section du contenu
Je veux créer des méthodes de portée et variables CAN accessibles par l'utilisateur, mais ne sont pas pertinentes aux étendues environnantes (frères et sœurs et les parents) en dehors du contexte de ma directive. Je voudrais également permettre à TOUTES les données de portée parent de couler de manière transparente.
Aucune: directives simples et en lecture seule
Je n'ai pas vraiment besoin de jouer avec les méthodes ou les variables de portée. Je fais probablement quelque chose qui n'a pas à voir avec les portées (comme l'affichage de plugins jQuery simples, la validation, etc.).
Remarques
ng-model=$parent.myVal
(enfant) oungModel: '='
(isoler).require: '^ngModel'
pour rechercher dans les éléments parents.la source
Après avoir écrit beaucoup de directives, j'ai décidé d'utiliser moins de
isolated
portée. Même si c'est cool et que vous encapsulez les données et assurez-vous de ne pas divulguer les données dans la portée parent, cela limite considérablement la quantité de directives que vous pouvez utiliser ensemble. Alors,Si la directive que vous allez écrire va se comporter entièrement d'elle-même et que vous ne la partagerez pas avec d'autres directives, optez pour une 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 qui contiennent des directives)
Si la directive que vous allez écrire va juste faire des manipulations dom qui n'ont besoin d'aucun état interne de portée, ou de modifications explicites de portée (surtout des choses très simples); optez pour aucune nouvelle portée . (tels que
ngShow
,ngMouseHover
,ngClick
,ngRepeat
)Si la directive que vous allez écrire doit changer certains éléments dans la portée parent, mais doit également gérer un état interne, optez pour une nouvelle portée enfant . (comme
ngController
)N'oubliez pas de consulter le code source des directives: https://github.com/angular/angular.js/tree/master/src/ng/directive
Cela aide grandement à savoir comment les penser
la source
require
, ce qui permet de découpler vos directives. Alors, comment cela limite-t-il les possibilités? Cela rend encore plus spécifiques les directives (déclarez donc de quoi vous dépendez). Je ne laisserais donc qu'une seule règle: si votre directive a un état ou a besoin de données de portée où elle est utilisée - utilisez une portée isolée. Sinon, n'utilisez pas la portée. Et à propos des "enfants" - j'ai également écrit pas mal de directives et je n'ai jamais eu besoin de cette fonctionnalité. Si "doit changer certains éléments dans la portée parent" - utilisez les liaisons.$parent
hack sale ). Donc, en réalité, les «étendues enfants» pour les directives sont quelque chose qui devrait être utilisé assez à l'arrière - commengRepeat
cela crée de nouvelles étendues enfants pour chaque élément à répéter (mais il le crée également en utilisantscope.$new();
et nonscope: true
.ngClick
etc.) Exiger crée une sorte de découplage, je suis d'accord, mais vous devez toujours être au courant de la directive parent. À moins que ce ne soit comme un composant , je suis contre l'isolement. Les directives (au moins la plupart d'entre elles) sont censées être hautement réutilisables et l'isolement rompt cela.Je pensais juste ajouter ma compréhension actuelle et comment elle se rapporte à d'autres concepts JS.
Par défaut (par exemple, non déclaré ou étendue: faux)
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.
portée:{}
C'est comme un module, tout ce qu'il veut utiliser doit être passé explicitement. Si chaque directive que vous utilisez est une portée isolée, cela peut être l'équivalent de créer chaque fichier JS que vous écrivez son propre module avec beaucoup de surcharge pour injecter toutes les dépendances.
portée: enfant
C'est le juste milieu entre les variables globales et le passthrough explicite. Il est similaire à la chaîne de prototypes de javascript et vous étend simplement une copie de la portée parent. Si vous créez une portée isolée et transmettez chaque attribut et fonction de la portée parent, cela est fonctionnellement équivalent à cela.
La clé est que N'IMPORTE QUELLE directive peut être écrite N'IMPORTE QUELLE manière. Les différentes déclarations de portée sont là pour vous aider à vous organiser. Vous pouvez tout faire un module, ou vous pouvez simplement utiliser toutes les variables globales et être très prudent. Pour faciliter la maintenance, il est préférable de modulariser votre logique en parties logiquement cohérentes.Il y a un équilibre entre une prairie ouverte et une prison fermée. La raison pour laquelle cela est délicat, je crois, c'est que lorsque les gens apprennent cela, ils pensent qu'ils apprennent comment fonctionnent les directives, mais en réalité ils apprennent l'organisation du code / de la logique.
Une autre chose qui m'a aidé à comprendre comment fonctionnent les directives est d'apprendre à propos de ngInclude. ngInclude vous aide à inclure des partiels html. Lorsque j'ai commencé à utiliser des directives, j'ai découvert que vous pouviez utiliser son option de modèle pour réduire votre code, mais je n'attachais vraiment 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, donc mon point de vue à ce sujet peut être complètement faux.
la source
Je suis d'accord avec Umur. En théorie, les étendues isolées semblent merveilleuses et "portables", mais en créant mon application pour impliquer des fonctionnalités non triviales, j'ai rencontré le besoin d'incorporer plusieurs directives (certaines imbriquées dans d'autres ou en y ajoutant des attributs) afin d'écrire entièrement dans mon propre HTML, qui est le but d'un langage spécifique au domaine.
En fin de compte, c'est trop bizarre d'avoir à passer chaque valeur globale ou partagée dans la chaîne avec plusieurs attributs à chaque appel DOM d'une directive (comme cela est requis avec la portée isolate). Il semble stupide d'écrire à plusieurs reprises tout cela dans le DOM et cela semble inefficace, même si ce sont des objets partagés. Cela complique également inutilement les déclarations de la directive. La solution de contournement de l'utilisation de $ parent pour "atteindre" et récupérer les valeurs de la directive HTML semble être une forme très mauvaise.
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 celles qui n'ont pas besoin d'accéder à quoi que ce soit du parent autre que ce qu'elles peuvent être transmises via des attributs simples et non répétitifs.
Ayant rêvé du rêve des langages spécifiques au domaine pendant des décennies avant qu'il y ait une telle chose, je suis ravi qu'AngularJS offre cette option et je sais que, comme plus de développeurs travaillent dans ce domaine, nous allons voir des applications très intéressantes qui sont également faciles à écrire, à développer et à déboguer pour leurs architectes.
-- RÉ
la source