web-dev-qa-db-fra.com

Une directive angulaire peut-elle transmettre des arguments à des fonctions dans des expressions spécifiées dans les attributs de la directive?

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

155
Ed Hinchliffe

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

210
Chandermani

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. 

57
ItsCosmo

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;
38
Ben

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. 

15
lex82

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.

0
jabko87