J'ai une directive de formulaire qui utilise un attribut callback
spécifié avec une portée d'isolement:
scope: { callback: '&' }
Il se trouve à l'intérieur d'un ng-repeat
, de sorte que l'expression que je passe inclut la id
de l'objet en tant qu'argument de la fonction de rappel:
<directive ng-repeat = "item in stuff" callback = "callback(item.id)"/>
Lorsque j'ai terminé avec la directive, il appelle $scope.callback()
à partir de sa fonction de contrôleur. Dans la plupart des cas, c'est correct et c'est tout ce que je veux faire, mais parfois, j'aimerais ajouter un autre argument à l'intérieur de la variable directive
elle-même.
Existe-t-il une expression angulaire qui autoriserait ceci: $scope.callback(arg2)
, entraînant l'appel de callback
avec arguments = [item.id, arg2]
?
Si non, quelle est la meilleure façon de le faire?
J'ai trouvé que cela fonctionne:
<directive
ng-repeat = "item in stuff"
callback = "callback"
callback-arg="item.id"/>
Avec
scope { callback: '=', callbackArg: '=' }
et l'appel de la directive
$scope.callback.apply(null, [$scope.callbackArg].concat([arg2, arg3]) );
Mais je ne pense pas que ce soit particulièrement intéressant et que cela implique d'ajouter des éléments supplémentaires dans le champ d'application isolé.
Y a-t-il un meilleur moyen?
Plunker playground here (laissez la console ouverte).
Si vous déclarez votre rappel mentionné par @ Lex82, comme
callback = "callback(item.id, arg2)"
Vous pouvez appeler la méthode de rappel dans la portée de la directive avec la mappe d'objet et la liaison sera effectuée correctement. Comme
scope.callback({arg2:"some value"});
sans nécessiter pour $ parse. Voir mon violon (journal de la console) http://jsfiddle.net/k7czc/2/
Update: Il y a un petit exemple de ceci dans la documentation :
& ou & attr - fournit un moyen d'exécuter une expression dans le contexte de la portée parente. Si aucun nom d'attr n'est spécifié, le nom d'attribut est supposé être le même que le nom local. Étant donné et définition du widget de la portée: { localFn: '& myAttr'}, puis isoler la propriété de portée localFn pointera sur un wrapper de fonction pour l'expression count = count + value. Souvent il est souhaitable de transmettre des données de la portée isolée via une expression et pour la portée parente, cela peut être fait en passant une carte de local noms de variables et valeurs dans le wrapper d'expression fn. Par exemple, Si l'expression est incrémentée (montant), nous pouvons spécifier le montant Valeur en appelant localFn en tant que localFn ({montant: 22}).
Rien à redire avec les autres réponses, mais j'utilise la technique suivante lors du passage de fonctions dans un attribut de directive.
Laissez la parenthèse lorsque vous incluez la directive dans votre code HTML:
<my-directive callback="someFunction" />
Puis "décompressez" la fonction dans le lien ou le contrôleur de votre directive. Voici un exemple:
app.directive("myDirective", function() {
return {
restrict: "E",
scope: {
callback: "&"
},
template: "<div ng-click='callback(data)'></div>", // call function this way...
link: function(scope, element, attrs) {
// unwrap the function
scope.callback = scope.callback();
scope.data = "data from somewhere";
element.bind("click",function() {
scope.$apply(function() {
callback(data); // ...or this way
});
});
}
}
}]);
L'étape de "décompression" permet à la fonction d'être appelée en utilisant une syntaxe plus naturelle. Cela garantit également que la directive fonctionne correctement, même si elle est imbriquée dans d'autres directives susceptibles de transmettre la fonction. Si vous n'avez pas décompressé, alors si vous avez un scénario comme celui-ci:
<outer-directive callback="someFunction" >
<middle-directive callback="callback" >
<inner-directive callback="callback" />
</middle-directive>
</outer-directive>
Vous vous retrouveriez alors avec quelque chose comme ceci dans votre directive interne:
callback()()()(data);
Ce qui échouerait dans d'autres scénarios d'imbrication.
J'ai adapté cette technique à partir d'un excellent article de Dan Wahlin à http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters
J'ai ajouté l'étape de décompression pour rendre l'appel de la fonction plus naturel et pour résoudre le problème de nidification que j'avais rencontré dans un projet.
En directive (myDirective
):
...
directive.scope = {
boundFunction: '&',
model: '=',
};
...
return directive;
Dans le modèle de directive:
<div
data-ng-repeat="item in model"
data-ng-click='boundFunction({param: item})'>
{{item.myValue}}
</div>
En source:
<my-directive
model='myData'
bound-function='myFunction(param)'>
</my-directive>
... où myFunction
est défini dans le contrôleur.
Notez que param
dans le modèle de directive se lie parfaitement à param
dans le source et est défini sur item
.
Pour appeler depuis la propriété link
d'une directive ("à l'intérieur" de celle-ci), utilisez une approche très similaire:
...
directive.link = function(isolatedScope) {
isolatedScope.boundFunction({param: "foo"});
};
...
return directive;
Oui, il existe un meilleur moyen: vous pouvez utiliser le service $ parse dans votre directive pour évaluer une expression dans le contexte de la portée parent tout en liant certains identifiants de l'expression à des valeurs visibles uniquement dans votre directive:
$parse(attributes.callback)(scope.$parent, { arg2: yourSecondArgument });
Ajoutez cette ligne à la fonction de liaison de la directive où vous pouvez accéder aux attributs de la directive.
Votre attribut de rappel peut alors être défini comme callback = "callback(item.id, arg2)"
car arg2 est lié à yourSecondArgument par le service $ parse de la directive. Des directives telles que ng-click
vous permettent d'accéder à l'événement click via l'identificateur $event
à l'intérieur de l'expression transmise à la directive en utilisant exactement ce mécanisme.
Notez qu'il n'est pas nécessaire que callback
soit membre de votre étendue isolée avec cette solution.
Pour moi, les suivants ont fonctionné:
en directive le déclarer comme ceci:
.directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
scope: {
myFunction: '=',
},
templateUrl: 'myDirective.html'
};
})
Dans le modèle de directive, utilisez-le de la manière suivante:
<select ng-change="myFunction(selectedAmount)">
Et ensuite, lorsque vous utilisez la directive, transmettez la fonction comme ceci:
<data-my-directive
data-my-function="setSelectedAmount">
</data-my-directive>
Vous transmettez la fonction par sa déclaration. La directive est appelée et les paramètres sont renseignés.