58 votes

La liaison bidirectionnelle AngularJS ne fonctionne pas dans la directive avec un champ d'application inclus

J'ai une zone de texte dans un contrôleur, qui est lié à modèle name. Il y a une directive à l'intérieur du contrôleur et il y a une autre zone de texte à l'intérieur de la directive, qui est lié à la même modèle name:

<div class="border" ng-controller="editCtrl">
   Controller: editCtrl <br/>
   <input type="text" ng-model="name" />
   <br/>
   <tabs>
      Directive: tabs <br/>
      <input type="text" ng-model="name"/>
   </tabs>
</div>

mod.directive('tabs', function() {
  return {
    restrict: 'E',
    transclude: true, 
    template:
      '<div class="border" ng-transclude></div>',
  };
});

Lorsque vous tapez quelque chose dans la grande zone de texte, il se reflète dans l'intérieur de la zone de texte, mais si vous tapez quelque chose dans l'intérieur de la zone de texte, il cesse de fonctionner, c'est à dire à la fois zone de texte ne reflète plus la même valeur.

Voir l'exemple à: http://jsfiddle.net/uzairfarooq/MNBLd/

J'ai aussi essayé d'utiliser une liaison bidirectionnelle attr (scope: {name: '='}), mais il donne une erreur de syntaxe.Et à l'aide de scope: {name: '@'} a le même effet.

Toute aide serait grandement appréciée.

En plus de la accepté de répondre, cet article m'a vraiment aidé dans la compréhension de l'héritage par prototype de l'enfant scpoes. Je recommande vivement toute personne ayant des problème avec des étendues de le lire en détail.

128voto

Mark Rajcok Points 85912

Une directive transclude: true les résultats dans la directive de la création d'une nouvelle (transcluded) enfant. Ce nouveau champ d'application fait hérite de la portée parent. Dans votre cas, le parent qui est à la portée associée à la editCtrl contrôleur.

En utilisant les deux sens de la liaison de données dans un enfant (c'est à dire, ng-model) pour le lier à un parent de la propriété qui contient une valeur primitive (par exemple, name) cause toujours des problèmes-et bien, je dois dire que ça ne fonctionne pas comme prévu. Lorsque l'étendue de la propriété est modifiée à l'enfant (par exemple, vous tapez dans la deuxième zone de texte), l'enfant crée un nouveau champ d'application de la propriété qui cache/les ombres de la portée parent propriété du même nom. Si la propriété parent est titulaire d'une valeur primitive, cette valeur est (essentiellement) copié à la propriété enfant lorsque l'enfant propriété est créée. Modifications futures de l'enfant (par exemple, la deuxième zone de texte) affectent uniquement la propriété enfant.

Avant de taper dans la deuxième zone de texte (c'est à dire, avant la modification de la propriété de l'enfant), l'enfant/transcluded portée trouve l' name propriété dans la portée parent via prototypes héritage (ligne en pointillés dans l'image ci-dessous). C'est pourquoi les deux zones de texte initialement rester en phase. Ci-dessous, si vous tapez "Marque" dans la première zone de texte, c'est ce que les étendues de ressembler à:

transcluded scope follows inheritance chain

J'ai créé un violon où vous pouvez examiner les deux étendues. Cliquez sur le "afficher le champ" lien à côté de la deuxième zone de texte avant de taper dans la deuxième zone de texte. Cela vous permettra de voir le transcluded enfant. Vous remarquerez qu'il n'a pas un name de la propriété à ce point. Effacer la console, tapez dans la deuxième zone de texte, puis cliquez sur le lien nouveau. Vous remarquerez que l'enfant a maintenant un name de la propriété, et la valeur initiale est la valeur de la propriété parent a ("Marque"). Si vous avez tapé "j'aime Angulaire" dans la deuxième zone de texte, c'est ce que les étendues de ressembler à:

transcluded primitive hides parent property

Il y a deux solutions:

  1. faire ce que @pgreen2 suggère (c'est la "meilleure pratique" de la solution) -- utiliser un objet au lieu d'une primitive. Lorsqu'un objet est utilisé, l'enfant/transcluded portée ne pas obtenir une nouvelle propriété. Seuls les prototypes de l'héritage est en jeu ici. Dans l'image ci-dessous, supposons que le editCtrl de dollars du champ d'application de cet objet défini:
    $scope.myObject = { name: "Mark", anotherProp: ... }:
    object in parent
  2. utiliser $parent de l'enfant (ce qui est un fragile de la solution, et n'est pas recommandé, car il fait des hypothèses sur la structure du document HTML): utilisez - ng-model="$parent.name" dans le <input> dans la <tabs> élément. La première image ci-dessus montre comment cela fonctionne.

Une erreur de syntaxe se produit lors de l'utilisation d' scope: {name: '='} parce que lors de l'utilisation de deux voies de liaison de données (c'est à dire, lors de l'utilisation de '='), l'interpolation n'est pas autorisée, c'est à dire, {{}} ne peut pas être utilisé. Au lieu de <tabs name="{{name}}"> utilisation <tabs name="name">.

L'utilisation de '@' fonctionne de la même que la transclude cas parce que ng-transclude utilise le transcluded portée, ne pas l'isoler de la portée qui est créé à l'aide de scope: { ... }.

Pour (beaucoup) plus d'informations sur les étendues (y compris les photos) voir
Quelles sont les nuances de portée prototypes / l'héritage par prototype dans AngularJS?

10voto

pgreen2 Points 1127

Je crois que le problème a à voir avec la portée. D'abord l'intérieure de la zone de texte n'a pas de name ensemble, de sorte qu'il est hérité de l'extérieur de la portée. C'est pourquoi, en tapant dans la boîte extérieure est reflété dans la boîte intérieure. Cependant, une fois de taper dans la boîte intérieure se produit, à l'intérieur de la portée contient désormais name ce qui signifie qu'il n'est plus lié à l'extérieur name alors que l'extérieur de la zone de texte n'a pas de synchronisation.

Le moyen le plus approprié pour résoudre stocke uniquement les modèles dans le champ d'application, pas vos valeurs. Je l'ai corrigé dans http://jsfiddle.net/pdgreen/5RVza/ L'astuce consiste à créer un objet de modèle (data) et le référencement de valeurs.

Le code incorrect modifie le champ d'application de la directive, le code correct modifie le modèle dans le champ d'application de la directive. Cette différence subtile permet à la portée de l'héritage pour fonctionner correctement.

Je crois que la façon Miško Hevery formulé a été, portée devrait être en écriture seulement dans le contrôleur, et en lecture seule dans les directives.

mise à jour: référence: https://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s

0voto

Umur Kontacı Points 12524

Une erreur de syntaxe signifie que vous avez mal écrit quelque chose. Il n'est pas lié à un framework / une bibliothèque en particulier. Vous avez probablement oublié d'ajouter "," ou de fermer une paranthèse. Vérifiez à nouveau

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