Directive isoler la portée avec la portée ng-repeat dans AngularJS


Deepak Nulu

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-repeatdirective. 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-repeatporté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 prioritypeut ê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-repeats'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-repeatdocumentation indique que cette directive s'exécute au niveau de priorité 1000. Il n'est pas clair s'il 1s'agit d'un niveau de priorité plus élevé ou d'un niveau de priorité inférieur. Lorsque j'ai utilisé le niveau de priorité 1dans 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 undefinedet ma directive n'affiche rien.

J'ai créé un violon pour montrer mon problème . J'ai commenté le prioritycadre de ma directive. J'ai une liste d'objets de nom et une directive appelée name-rowqui 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 selectedvariable dans la portée principale. Le tableau de noms, la selectedvariable sont passés à la name-rowdirective 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 selectedtrouve à 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-repeatde 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é 2000dans ma directive ne fonctionne-t-il pas?
  • En utilisant Batarang, est-il possible de savoir quel type de lunette est utilisé?
Josh David Miller

D'accord, à travers beaucoup de commentaires ci-dessus, j'ai découvert la confusion. Tout d'abord, quelques points de clarification:

  • ngRepeat n'affecte pas la portée d'isolat choisie
  • les paramètres passés dans ngRepeat pour une utilisation sur les attributs de votre directive n'utilisent une portée prototypiquement héritée
  • la raison pour laquelle votre directive ne fonctionne pas n'a rien à voir avec la portée d'isolat

Voici un exemple du même code mais avec la directive supprimée:

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

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 selectedde 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.

entrez la description de l'image ici

Après avoir cliqué sur le premier élément, les étendues ressemblent maintenant à ceci:

entrez la description de l'image ici

Notez qu'une nouvelle selectedproprié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:

entrez la description de l'image ici


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:

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Voici un deuxième JSFiddle montrant que cela fonctionne aussi.

Voici à quoi ressemblent les portées initialement:

entrez la description de l'image ici

Après avoir cliqué sur le premier élément:

entrez la description de l'image ici

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:

entrez la description de l'image ici

Scopes après avoir cliqué sur le premier élément:

entrez la description de l'image ici

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 ..

Articles connexes


isoler la portée dans la directive non définie

patriques J'ai du mal à créer une directive, car la portée d'isolat n'est pas passée du html et me donne undefined dans le contrôleur de directives. Définition de lmLoop angular.module('loops.directives') .directive('lmLoop', function() { return { res

Portée AngularJS 1.5 dans la directive ng-include

Miuosh Voici du code http://jsfiddle.net/miuosh/n6yppypj/ avec la directive uploadfile. Le problème est que j'utilise ceci <input type="file" file-model="myFile" /> <button ng-click="uploadFile()">upload me</button> dans ng-include = "'views / uploadFileF