AngularJS - passe la fonction à la directive

160

J'ai un exemple angularJS

<div ng-controller="testCtrl">

<test color1="color1" updateFn="updateFn()"></test>
</div>
 <script>
  angular.module('dr', [])
.controller("testCtrl", function($scope) {
    $scope.color1 = "color";
    $scope.updateFn = function() {
        alert('123');
    }
})
.directive('test', function() {
    return {
        restrict: 'E',
        scope: {color1: '=',
                updateFn: '&'},
        template: "<button ng-click='updateFn()'>Click</button>",
        replace: true,
        link: function(scope, elm, attrs) { 
        }
    }
});

</script>
</body>

</html>

Je veux que lorsque je clique sur le bouton, la boîte d'alerte apparaît, mais rien ne s'affiche.

Quelqu'un peut-il m'aider?

user2707026
la source

Réponses:

243

Pour appeler une fonction de contrôleur dans la portée parent à partir d'une directive de portée isolate, utilisez dash-separated noms d'attribut dans le HTML comme le dit l'OP.

Aussi si vous souhaitez envoyer un paramètre à votre fonction, appelez la fonction en passant un objet:

<test color1="color1" update-fn="updateFn(msg)"></test>

JS

var app = angular.module('dr', []);

app.controller("testCtrl", function($scope) {
    $scope.color1 = "color";
    $scope.updateFn = function(msg) {        
        alert(msg);
    }
});

app.directive('test', function() {
    return {
        restrict: 'E',
        scope: {
            color1: '=',
            updateFn: '&'
        },
        // object is passed while making the call
        template: "<button ng-click='updateFn({msg : \"Hello World!\"})'>
            Click</button>",
        replace: true,        
        link: function(scope, elm, attrs) {             
        }
    }
});

Fiddle

AlwaysALearner
la source
1
Merci Codezilla pour votre réponse, et je veux poser des questions sur les circonstances lorsque je veux lier la fonction "updateFn" de la portée parent pour isoler la portée dans la directive "test", est-ce possible?
user2707026
2
L' replaceattribut est obsolète dans AngularJS: stackoverflow.com/questions/24194972/…
cdmckay
8
pour une raison quelconque, l'argument n'est pas défini pour moi.
chovy
1
@chovy Je pense que l'argument n'est utilisé qu'une fois que vous appelez à nouveau la méthode? La première utilisation des crochets ouverts semble être le format que angular veut pour que la méthode soit simplement passée, mais je me trompe peut-être
marksyzm
1
Un mappage d'objet updateFn({msg: 'my message'});doit être utilisé dans ce format lors de l'appel de fonction à l'intérieur de la linkfonction de la directive .
Brian
159

Il me manque peut-être quelque chose, mais bien que les autres solutions appellent la fonction de portée parent, il n'y a pas de possibilité de passer des arguments à partir du code de directive, c'est parce que l' update-fnappel est updateFn()avec des paramètres fixes, par exemple {msg: "Hello World"}. Un léger changement permet à la directive de transmettre des arguments, ce que je pense être beaucoup plus utile.

<test color1="color1" update-fn="updateFn"></test>

Notez que le HTML passe une référence de fonction, c'est-à-dire sans ()crochets.

JS

var app = angular.module('dr', []);

app.controller("testCtrl", function($scope) {
    $scope.color1 = "color";
    $scope.updateFn = function(msg) {        
        alert(msg);
    }
});

app.directive('test', function() {
    return {
        restrict: 'E',
        scope: {
            color1: '=',
            updateFn: '&'
        },
        // object is passed while making the call
        template: "<button ng-click='callUpdate()'>
            Click</button>",
        replace: true,        
        link: function(scope, elm, attrs) {       
          scope.callUpdate = function() {
            scope.updateFn()("Directive Args");
          }
        }
    }
});

Ainsi, dans ce qui précède, le HTML appelle la callUpdatefonction de portée locale , qui `` récupère '' ensuite le updateFn de la portée parent et appelle la fonction retournée avec des paramètres que la directive peut générer.

http://jsfiddle.net/mygknek2/

Steve
la source
9
Je ne sais pas comment je peux obtenir un vote négatif pour quelque chose qui fonctionne? Vous devriez laisser un commentaire si vous allez voter.
steve
7
Cela a fonctionné pour moi. Si vous ne voulez pas la fonction supplémentaire, écrivez simplementng-click="updateFn()('Directive Args')"
Graham Walters
7
Awwww! scope.updateFn () ("Directive Args"); !! NOT scope.updateFn ("Directive Args"); !!!
Phung D. An
2
C'est en effet une réponse plus parfaite !!
vinesh
11
@ Ludwik11 bien sûr - c'est parce que scope.updateFn lorsqu'elle est définie comme ceci est une fonction qui renvoie une fonction (d'où le () ()) et c'est parce que nous passons dans scope (via update-fn = "updateFn" en html) une référence à la fonction que nous voulons appeler. Le 1er () est un appel à angular pour renvoyer cette référence, le 2nd () fait l'appel à notre fonction et est l'endroit où nous passons tous les paramètres. HTH
steve
39

Dans votre balise Html directive 'test', le nom d'attribut de la fonction ne doit pas être camelCased, mais basé sur des tirets.

donc - au lieu de:

<test color1="color1" updateFn="updateFn()"></test>

écrire:

<test color1="color1" update-fn="updateFn()"></test>

C'est la manière angulaire de faire la différence entre les attributs de directive (comme la fonction update-fn) et les fonctions.

Ofer Segev
la source
1
merci pour la capture. J'ai inclus cela dans ma réponse. Voté! :)
AlwaysALearner
10

Que diriez-vous de passer la fonction de contrôleur avec la liaison bidirectionnelle ? Ensuite, vous pouvez l'utiliser dans la directive exactement de la même manière que dans un modèle normal (j'ai supprimé les parties non pertinentes pour plus de simplicité):

<div ng-controller="testCtrl">

   <!-- pass the function with no arguments -->
   <test color1="color1" update-fn="updateFn"></test>
</div>

<script>
   angular.module('dr', [])
   .controller("testCtrl", function($scope) {
      $scope.updateFn = function(msg) {
         alert(msg);
      }
   })
   .directive('test', function() {
      return {
         scope: {
            updateFn: '=' // '=' bidirectional binding
         },
         template: "<button ng-click='updateFn(1337)'>Click</button>"
      }
   });
</script>

J'ai atterri à cette question, parce que j'ai essayé la méthode ci-dessus befire, mais cela n'a pas fonctionné. Maintenant, cela fonctionne parfaitement.

Márton Tamás
la source
5

utilisez le tiret et les minuscules pour le nom de l'attribut (comme l'ont dit d'autres réponses):

 <test color1="color1" update-fn="updateFn()"></test>

Et utilisez "=" au lieu de "&" dans la portée de la directive:

 scope: { updateFn: '='}

Ensuite, vous pouvez utiliser updateFn comme toute autre fonction:

 <button ng-click='updateFn()'>Click</button>

Voilà!

Jeanne
la source
5
Pourquoi utiliseriez-vous '=' au lieu de '&'? quand j'ai essayé cela, il a continué d'appeler ma fonction à plusieurs reprises.
user1012500
2
Il est faux d'utiliser '=' pour cela. C'est pour la liaison d'objet bidirectionnelle.
Ben Taliadoros
1
Je pense que le seul problème est l'utilisation de parenthèses dans le premier modèle. Cela exécute la fonction puis lie le résultat. Au lieu de cela, vous ne devriez passer que le nom de la fonction, comme ceci:update-fn="updateFn"
Márton Tamás
1
Mauvaise réponse. très mauvais.
1er
4

J'ai dû utiliser la liaison "=" au lieu de "&" car cela ne fonctionnait pas. Comportement étrange.

Gunter Reinitzer
la source
2
Cela est dû au fait que vous transmettez probablement à la directive une référence de fonction JS au lieu de l'exécution. Lorsque vous passez la fonction en tant qu'argument à la directive, update-fn="updateFn()"vous devez inclure les parenthèses (et peut-être les paramètres). Le passer en tant que référence de fonction update-fn="updateFn"ne fonctionnera pas avec la &liaison
JorgeGRC
0

@JorgeGRC Merci pour votre réponse. Une chose cependant, la partie «peut-être» est très importante. Si vous avez des paramètres, vous devez également les inclure dans votre modèle et vous assurer de spécifier vos sections locales, par exemple updateFn({msg: "Directive Args"}.

user2893858
la source