95 votes

Directive isoler le champ d'application avec le champ d'application ng-repeat

J'ai une directive avec un isolat-portée (de sorte que je peux réutiliser la directive dans d'autres lieux), et quand j'utilise cette directive avec une ng-repeat, cela ne fonctionne pas.

J'ai lu toute la documentation et stackoverflow réponses sur ce sujet et de comprendre les enjeux. Je crois que j'ai évité tous les pièges.

Donc, je comprends que mon code ne fonctionne pas parce que le champ créé par l' ng-repeat directive. Mon propre directive crée un isolat-champ d'application et fait dans les deux sens de la liaison de données à un objet de la portée parent. Mon directive assigne un nouvel objet-valeur de cette variable liée et cela fonctionne parfaitement lors de mon directive est utilisée sans ng-repeat (la variable parent est correctement mis à jour). Cependant, avec ng-repeat, la cession crée une nouvelle variable dans l' ng-repeat de la portée et de la variable parent ne voit pas le changement. Tout cela est prévu sur la base de ce que j'ai lu.

J'ai aussi lu que lorsqu'il y a plusieurs directives sur un élément donné, qu'un seul champ est créé. Et qu'un priority peut être définie dans chaque directive pour définir l'ordre dans lequel les directives sont appliquées; les directives sont classées par ordre de priorité, puis leur compiler les fonctions sont appelés (de Recherche pour le mot priorité à http://docs.angularjs.org/guide/directive).

Alors j'espérais que je pourrais utiliser en priorité pour faire en sorte que mon directive s'exécute en premier et finit par créer un isolat-champ, et quand ng-repeat s'exécute, il ré-utilise l'isolat-champ d'application au lieu de la création d'un champ qui fait hérite de la portée parent. L' ng-repeat documentation indique que celle-ci s'exécute à un niveau de priorité 1000. Il n'est pas clair si 1 est un niveau de priorité plus élevé ou un faible niveau de priorité. Lorsque j'ai utilisé le niveau de priorité 1 dans mon directive, il n'est pas de faire une différence, alors j'ai essayé 2000. Mais cela ne fait qu'aggraver les choses: mon liaisons bidirectionnelles deviennent undefined et mon directive ne pas afficher quoi que ce soit.

J'ai créé un violon pour montrer mon problème. J'ai commenté l' priority paramètre dans mon directive. J'ai une liste de nom des objets et une directive appelée name-row qui montre les champs prénom et nom dans le nom de l'objet. Quand un nom affiché est cliqué, je le veux pour définir un selected variable dans le champ d'application principal. Le tableau de noms, l' selected variable sont passés à l' name-row directive à l'aide de deux voies de liaison de données.

Je sais comment obtenir que cela fonctionne par appel à des fonctions dans le champ d'application principal. Je sais aussi que si selected est à l'intérieur d'un autre objet, et je la lie à l'objet extérieur, les choses allaient tourner. Mais je ne suis pas intéressé par ces solutions à l'heure actuelle.

Au lieu de cela, les questions que j'ai sont:

  • Comment puis-je empêcher ng-repeat de la création d'un champ qui fait hérite de la portée parent, et, au lieu de l'avoir utiliser mon directive isoler le-champ?
  • Pourquoi le niveau de priorité 2000 dans mon directive ne fonctionne pas?
  • À l'aide de Batarang, est-il possible de savoir quel type de champ d'application est en cours d'utilisation?

Je vous remercie.

-deepak

203voto

Josh David Miller Points 66508

Ok, à travers un grand nombre de commentaires ci-dessus, j'ai découvert la confusion. Tout d'abord, quelques points d'éclaircissement:

  • ngRepeat n'affecte pas votre choisi d'isoler la portée
  • les paramètres passés dans ngRepeat pour une utilisation sur votre directive attributs de faire usage d'un fait, héritée de la portée
  • la raison de votre directive ne fonctionne pas n'a rien à voir avec l'isolat portée

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 fonctionne pas. Vous obtenez les mêmes résultats que dans la directive.

Pourquoi ne pas travailler? Parce que les étendues dans AngularJS utiliser l'héritage par prototype. La valeur selected sur votre parent est une primitive. En JavaScript, ce qui signifie qu'il sera écrasé lorsqu'un enfant définit de la même valeur. Il y a une règle d'or dans AngularJS étendues des valeurs de modèle devrait toujours avoir un . . Qui est, ils ne doivent jamais être primitives. Voir cette SORTE de réponse pour plus d'informations.


Voici une photo de ce que les étendues d'abord ressembler.

enter image description here

Après avoir cliqué sur le premier point, les étendues maintenant ressembler à ceci:

enter image description here

Notez qu'un nouveau selected de la propriété a été créé sur le ngRepeat portée. Le contrôleur de portée 003 a pas été modifié.

Vous pouvez probablement deviner ce qui se passe lorsque l'on clique sur le deuxième point:

enter image description here


Si votre problème n'est pas réellement causé par ngRepeat à tous - il est causé par la rupture d'une règle d'or dans AngularJS. Le moyen pour résoudre ce problème est d'utiliser simplement une propriété de l'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 une deuxième JSFiddle montrant cela fonctionne aussi.

Voici ce que les étendues de ressembler au départ:

enter image description here

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

enter image description here

Ici, le contrôleur de la portée en est affectée, comme souhaité.

Aussi, pour prouver que cela va continuer à travailler avec votre directive avec un isolat de la portée (car, encore une fois, cela n'a rien à voir avec votre problème, ici, est un JSFiddle pour que, aussi, la vue doit refléter l'objet. Vous remarquerez que le seul changement nécessaire était d'utiliser un objet au lieu d'une primitive.

Les étendues d'abord:

enter image description here

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

enter image description here

Pour conclure: encore une fois, le problème n'est pas avec l'isolat champ d'application et il n'est pas avec la façon dont ngRepeat œuvres. Votre problème est que vous êtes briser une règle qui est connu pour conduire à ce problème. Les modèles en AngularJS devrait toujours avoir un ..

6voto

Alex Osborn Points 2756

Sans directement en essayant d'éviter de répondre à vos questions, au lieu de prendre un coup d'oeil à la suite de violon:

http://jsfiddle.net/dVPLM/

Le point clé est qu'au lieu d'essayer de combattre et de modifier le comportement classique Angulaire, vous pouvez structurer votre directive de travailler avec ng-repeat , au lieu d'essayer de le remplacer.

Dans votre template:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

Dans votre directive:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

En réponse à vos questions:

  • ng-repeat permettra de créer un champ d'application, vous ne devriez pas essayer de changer cela.
  • La priorité dans les directives n'est pas seulement l'exécution de la commande - voir: Comment Angulaire du compilateur HTML organiser l'ordre de la compilation?
  • Dans Batarang, si vous cochez l'onglet performances, vous pouvez voir les expressions liés pour chaque portée, et de vérifier si cela correspond à vos attentes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X