J'ai une directive avec une portée isolée (pour que je puisse réutiliser la directive dans d'autres endroits), et lorsque j'utilise cette directive avec un ng-repeat
, cela ne fonctionne pas.
J'ai lu toute la documentation et les réponses de Stack Overflow sur ce sujet et je comprends les problèmes. Je crois que j'ai évité tous les pièges habituels.
Je comprends donc que mon code échoue à cause de la portée créée par la ng-repeat
directive. Ma propre directive crée une portée isolate et effectue une liaison de données bidirectionnelle à un objet de la portée parent. Ma directive attribuera une nouvelle valeur d'objet à cette variable liée et cela fonctionne parfaitement lorsque ma directive est utilisée sans ng-repeat
(la variable parente est mise à jour correctement). Cependant, avec ng-repeat
, l'affectation crée une nouvelle variable dans la ng-repeat
portée et la variable parent ne voit pas le changement. Tout cela est comme prévu d'après ce que j'ai lu.
J'ai également lu que lorsqu'il y a plusieurs directives sur un élément donné, une seule portée est créée. Et que a priority
peut être défini dans chaque directive pour définir l'ordre dans lequel les directives sont appliquées; les directives sont triées par priorité et ensuite leurs fonctions de compilation sont appelées (recherchez le mot priority sur http://docs.angularjs.org/guide/directive ).
J'espérais donc pouvoir utiliser la priorité pour m'assurer que ma directive s'exécute en premier et finit par créer une portée isolate, et lorsqu'elle ng-repeat
s'exécute, elle réutilise la portée isolate au lieu de créer une portée qui hérite prototypiquement de la portée parent. La ng-repeat
documentation indique que cette directive s'exécute au niveau de priorité 1000
. Il n'est pas clair s'il 1
s'agit d'un niveau de priorité plus élevé ou d'un niveau de priorité inférieur. Lorsque j'ai utilisé le niveau de priorité 1
dans ma directive, cela ne faisait aucune différence, alors j'ai essayé 2000
. Mais cela ne fait qu'empirer les choses: mes liaisons bidirectionnelles deviennent undefined
et ma directive n'affiche rien.
J'ai créé un violon pour montrer mon problème . J'ai commenté le priority
cadre de ma directive. J'ai une liste d'objets de nom et une directive appelée name-row
qui montre les champs de nom et prénom dans l'objet de nom. Lorsqu'un nom affiché est cliqué, je veux qu'il définisse une selected
variable dans la portée principale. Le tableau de noms, la selected
variable sont passés à la name-row
directive en utilisant une liaison de données bidirectionnelle.
Je sais comment faire fonctionner cela en appelant des fonctions dans la portée principale. Je sais aussi que si se selected
trouve à l'intérieur d'un autre objet et que je me lie à l'objet extérieur, les choses fonctionneraient. Mais je ne suis pas intéressé par ces solutions pour le moment.
Au lieu de cela, les questions que j'ai sont:
- Comment puis-je empêcher
ng-repeat
de créer une étendue qui hérite de manière prototypique de l'étendue parente et lui faire utiliser à la place l'étendue isolate de ma directive? - Pourquoi le niveau de priorité
2000
dans ma directive ne fonctionne-t-il pas? - En utilisant Batarang, est-il possible de savoir quel type de lunette est utilisé?
scope: true
pour votre directive. Voir aussi (si vous ne l'avez pas déjà fait) stackoverflow.com/questions/14914213/... De plus, ce n'est pas parce qu'une directive sera utilisée à plusieurs endroits que nous devrions automatiquement utiliser une portée isolate.ng-repeat
. Je pense qu'il est utile de pouvoir mélanger des directives autonomes avecng-repeat
. A suivre ...ng-repeat
ne devrait pas avoir de portée.ng-repeat
avoir une portée a du sens pour le cas d'utilisation typique, donc je ne suggère pas qu'il soit changé. Au lieu de cela, comme je l'ai commenté dans la réponse d'Alex Osborn, je pense que je vais créer une directive répétée surng-repeat
cette base qui ne crée pas sa propre portée. Cela peut ensuite être utilisé pour répéter des directives qui ont leurs propres étendues isolées. À suivre ...ng-repeat
ou la directive de répétition sans portée personnalisée. Je pense qu'il est normal que "l'appelant" sache cela, mais ce n'est pas correct pour un "appelé" (la directive étant répétée) de savoir si elle est répétée ou non. À suivre ...Réponses:
D'accord, à travers beaucoup de commentaires ci-dessus, j'ai découvert la confusion. Tout d'abord, quelques points de clarification:
Voici un exemple du même code mais avec la directive supprimée:
Voici un JSFiddle démontrant que cela ne fonctionnera pas. Vous obtenez exactement les mêmes résultats que dans votre directive.
Pourquoi ça ne marche pas? Parce que les étendues dans AngularJS utilisent l'héritage prototypique. La valeur
selected
de votre étendue parent est une primitive . En JavaScript, cela signifie qu'il sera écrasé lorsqu'un enfant définit la même valeur. Il existe une règle d'or dans les étendues AngularJS: les valeurs de modèle doivent toujours avoir un.
. Autrement dit, ils ne devraient jamais être des primitifs. Voir cette réponse SO pour plus d'informations.Voici une image de ce à quoi ressemblent initialement les oscilloscopes.
Après avoir cliqué sur le premier élément, les étendues ressemblent maintenant à ceci:
Notez qu'une nouvelle
selected
propriété a été créée sur l'étendue ngRepeat. La portée du contrôleur 003 n'a pas été modifiée.Vous pouvez probablement deviner ce qui se passe lorsque nous cliquons sur le deuxième élément:
Donc, votre problème n'est pas du tout causé par ngRepeat - il est causé par une violation d'une règle d'or dans AngularJS. La façon de résoudre ce problème consiste simplement à utiliser une propriété d'objet:
Voici un deuxième JSFiddle montrant que cela fonctionne aussi.
Voici à quoi ressemblent les portées initialement:
Après avoir cliqué sur le premier élément:
Ici, la portée du contrôleur est affectée, comme souhaité.
Aussi, pour prouver que cela fonctionnera toujours avec votre directive avec une portée d'isolat (car, encore une fois, cela n'a rien à voir avec votre problème), voici un JSFiddle pour cela aussi, la vue doit refléter l'objet. Vous noterez que le seul changement nécessaire était d'utiliser un objet au lieu d'une primitive .
Portées initialement:
Scopes après avoir cliqué sur le premier élément:
Pour conclure: encore une fois, votre problème n'est pas avec la portée d'isolat et ce n'est pas avec le fonctionnement de ngRepeat. Votre problème est que vous enfreignez une règle connue pour mener à ce problème. Les modèles dans AngularJS doivent toujours avoir un fichier
.
.la source
.
règle d'or n'est requise que pour les étendues qui ont un héritage prototypique. Avec isolate-scopes, les variables qui vous intéressent sont définies dans lascope: {}
définition de la directive, et donc ces variables existeront dans l'isolate-scope depuis le début. En outre, ils ne masquent pas les variables parentes, car la portée isolate n'hérite pas de manière prototypique de la portée parent. c'est-à-dire qu'il n'y a pas de portée "parent" à masquer..
.Sans essayer directement d'éviter de répondre à vos questions, jetez plutôt un œil au violon suivant:
http://jsfiddle.net/dVPLM/
Le point clé est qu'au lieu d'essayer de lutter et de changer le comportement conventionnel d'Angular, vous pouvez structurer votre directive pour travailler avec
ng-repeat
plutôt que d'essayer de la remplacer.Dans votre modèle:
Dans votre directive:
En réponse à vos questions:
ng-repeat
créera une portée, vous ne devriez vraiment pas essayer de changer cela.la source
ng-repeat
ou non. Peut-être que lorsqu'il est écrit pour la première fois, il n'est jamais utilisé avecng-repeat
. Et dans le futur, il pourrait être utilisé avecng-repeat
, et à ce moment-là, il devrait simplement fonctionner sans réécriture. Espérons qu'une future version d'AngularJS rendra cela possible.ng-repeat
ou non. Mais il semble que je devrais passer une fonction à la directive afin qu'elle puisse modifier les variables dans la portée parent, au lieu de pouvoir muter une liaison bidirectionnelle dans la propre portée de la directive. La liaison bidirectionnelle et les directives réutilisables sont mes deux choses préférées à propos d'AngularJS etng-repeat
se révèlent être une mouche dans la pommade. Peut-être que je peux écrire unng-repeat
équivalent qui ne crée pas sa propre portée.