En drinkLonghand
, vous devez utiliser le code
scope.flavor = attrs.flavor;
Lors de la liaison phase interpolée attributs n'ont pas encore été évalué, de sorte que leurs valeurs sont undefined
. (Ils travaillent à l'extérieur de l' ng-repeat
parce que dans ces cas, vous n'utilisez pas la chaîne de l'interpolation, que vous soyez simplement de passage dans une chaîne ordinaire, par exemple, "fraise".) Ceci est mentionné dans les Directives du guide du développeur, avec une méthode sur Attributes
qui n'est pas présent dans la documentation de l'API appelée $observe
:
Utiliser $observe
d'observer les changements de valeur des attributs qui contiennent de l'interpolation (par exemple, src="{{bar}}"
). Ce n'est pas seulement très efficace mais elle est aussi la seule façon d'obtenir facilement la valeur réelle, car lors de la liaison de la phase de l'interpolation n'a pas encore été évalué et donc la valeur est en ce moment à l' undefined
.
Donc, pour résoudre ce problème, votre drinkLonghand
directive devrait ressembler à ceci:
app.directive("drinkLonghand", function() {
return {
template: '<div>{{flavor}}</div>',
link: function(scope, element, attrs) {
attrs.$observe('flavor', function(flavor) {
scope.flavor = flavor;
});
}
};
});
Cependant, le problème, c'est qu'il n'utilise pas un isolat portée; ainsi, la ligne
scope.flavor = flavor;
a le potentiel de remplacer un pré-existante de la variable dans le champ d'application nommée flavor
. En ajoutant un espace isoler portée aussi ne fonctionne pas; c'est parce Angulaire tentatives pour interpoler la chaîne en fonction du champ de la directive, sur laquelle il n'y a pas d'attribut appelé flav
. (Vous pouvez le tester en ajoutant scope.flav = 'test';
au-dessus de l'appel à l' attrs.$observe
.)
Bien sûr, vous pouvez résoudre ce problème avec un isolat de définition de la portée comme
scope: { flav: '@flavor' }
ou par la création d'un non-isoler enfant
scope: true
ou en ne comptant pas sur un template
avec {{flavor}}
et au lieu de faire des manipulations du DOM, comme
attrs.$observe('flavor', function(flavor) {
element.text(flavor);
});
mais cela va à l'encontre de l'objectif de l'exercice (par exemple, il serait plus facile d'utiliser l' drinkShortcut
méthode). Donc, pour faire de cette directive de travail, nous allons sortir de l' $interpolate
service pour faire de l'interpolation nous-mêmes de la directive $parent
portée:
app.directive("drinkLonghand", function($interpolate) {
return {
scope: {},
template: '<div>{{flavor}}</div>',
link: function(scope, element, attrs) {
// element.attr('flavor') == '{{flav}}'
// `flav` is defined on `scope.$parent` from the ng-repeat
var fn = $interpolate(element.attr('flavor'));
scope.flavor = fn(scope.$parent);
}
};
});
Bien sûr, cela ne fonctionne que pour la valeur initiale de l' scope.$parent.flav
; si la valeur est capable de changer, vous devez utiliser $watch
et de réévaluer le résultat de l'interpolation de la fonction fn
(je ne suis pas positif sur le dessus de ma tête comment vous savez quoi $watch
; vous pourriez avoir à passer dans une fonction). scope: { flavor: '@' }
est un raccourci pratique pour éviter d'avoir à gérer toute cette complexité.
[Mise à jour]
Pour répondre à la question dans les commentaires:
Comment est le raccourci de la méthode de résolution de ce problème en coulisses? Est-ce à l'aide de l' $interpoler le service que vous avez fait, ou est-il en train de faire quelque chose d'autre?
Je n'étais pas sûr à ce sujet, donc j'ai cherché dans la source. J'ai trouvé le suivant en compile.js
:
forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) {
var match = definiton.match(LOCAL_REGEXP) || [],
attrName = match[2]|| scopeName,
mode = match[1], // @, =, or &
lastValue,
parentGet, parentSet;
switch (mode) {
case '@': {
attrs.$observe(attrName, function(value) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
break;
}
Il semble donc que, attrs.$observe
peut être dit en interne pour utiliser un champ d'application différent de celui en cours à la base de l'attribut d'observation sur (l'avant-dernière ligne, au-dessus de l' break
). Il peut être tentant d'utiliser vous-même, gardez à l'esprit que tout ce qui a le double dollar- $$
préfixe doit être considéré comme privé Angulaire privée de l'API, et sont sujettes à modification sans avertissement (ne pas mentionner que vous obtenez ce pour libre de toute façon lors de l'utilisation de l' @
mode).