AngularJS - pasar función a directiva

160

Tengo un ejemplo 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>

Cuando haga clic en el botón Deseo, aparecerá el cuadro de alerta, pero no se mostrará nada.

¿Alguien puede ayudarme?

usuario2707026
fuente

Respuestas:

243

Para llamar a una función de controlador en el ámbito primario desde el interior de una directiva de ámbito aislado, utilice dash-separatednombres de atributos en el HTML como lo dijo el OP.

Además, si desea enviar un parámetro a su función, llame a la función pasando un objeto:

<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
fuente
1
Agradezco a Codezilla por su respuesta, y quiero preguntar acerca de la circunstancia en la que quiero vincular la función "updateFn" del ámbito primario para aislar el alcance en la directiva "prueba", ¿es eso posible?
user2707026
2
El replaceatributo ha quedado en desuso en AngularJS: stackoverflow.com/questions/24194972/…
cdmckay
8
Por alguna razón, el argumento no está definido para mí.
Chovy
1
@chovy ¿Creo que el argumento solo se usa una vez que vuelves a llamar al método? El primer uso de corchete abierto parece ser el formato que angular quiere para que se pase el método, pero podría estar equivocado allí
marksyzm
1
Se updateFn({msg: 'my message'});debe usar un mapeo de objetos en ese formato cuando se realiza la llamada a la función dentro de la linkfunción de la directiva .
Brian
159

Tal vez me falta algo, pero aunque las otras soluciones sí llaman a la función de alcance principal, no hay capacidad para pasar argumentos desde el código de la directiva, esto se debe a que update-fnestá llamando updateFn()con parámetros fijos, por ejemplo {msg: "Hello World"}. Un ligero cambio permite que la directiva pase argumentos, lo que creo que es mucho más útil.

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

Tenga en cuenta que el HTML pasa una referencia de función, es decir, sin ()corchetes.

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");
          }
        }
    }
});

Entonces, en lo anterior, el HTML está llamando a la callUpdatefunción de ámbito local , que luego 'obtiene' el updateFn del ámbito primario y llama a la función devuelta con parámetros que la directiva puede generar.

http://jsfiddle.net/mygknek2/

Steve
fuente
9
¿No estoy seguro de cómo puedo obtener un voto negativo por algo que funciona? Debería dejar un comentario si va a rechazar el voto.
Steve
77
Esto funcionó para mí. Si no quieres la función extra solo escribeng-click="updateFn()('Directive Args')"
Graham Walters
77
Awwww! scope.updateFn () ("Args Directiva"); !! NOT scope.updateFn ("Argumentos de directiva"); !!!
Phung D. Un
2
¡Esta es la respuesta más perfecta!
vinesh
11
@ Ludwik11 seguro: es porque scope.updateFn cuando se define así es una función que devuelve una función (de ahí el () ()) y esto es porque pasamos al alcance (a través de update-fn = "updateFn" en html) una referencia a la función que queremos llamar. El 1st () es una llamada a angular para devolver esta referencia, el 2nd () realiza la llamada a nuestra función y es donde pasamos cualquier parámetro. HTH
steve
39

En su etiqueta Html de la directiva 'prueba', el nombre del atributo de la función no debe ser camelCased, sino basado en guiones.

entonces, en lugar de:

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

escribir:

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

Esta es la forma angular de distinguir entre los atributos de la directiva (como la función update-fn) y las funciones.

Ofer Segev
fuente
1
Gracias por la captura. Lo he incluido en mi respuesta. ¡Votado! :)
AlwaysALearner
10

¿Qué tal pasar la función del controlador con enlace bidireccional ? Luego puede usarlo en la directiva exactamente de la misma manera que en una plantilla normal (eliminé las partes irrelevantes por simplicidad):

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

Aterricé en esta pregunta, porque probé el método anterior, pero de alguna manera no funcionó. Ahora funciona a la perfección.

Márton Tamás
fuente
5

use guiones y minúsculas para el nombre del atributo (como dicen otras respuestas):

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

Y use "=" en lugar de "&" en el alcance de la directiva:

 scope: { updateFn: '='}

Luego puede usar updateFn como cualquier otra función:

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

Ahí tienes!

Jane
fuente
55
¿Por qué usarías '=' en lugar de '&'? cuando probé esto, siguió llamando repetidamente a mi función.
user1012500
2
Está mal usar '=' para esto. Eso es para el enlace bidireccional de objetos.
Ben Taliadoros
1
Creo que el único problema es el uso de paréntesis en la primera plantilla. Esto ejecuta la función y luego vincula el resultado. En su lugar, debe pasar solo el nombre de la función, de esta manera:update-fn="updateFn"
Márton Tamás
1
Mala respuesta. muy mal.
Towry
4

Tuve que usar el enlace "=" en lugar de "&" porque eso no funcionaba. Comportamiento extraño.

Gunter Reinitzer
fuente
2
Esto se debe a que lo más probable es que le pase a la directiva una referencia de función JS en lugar de la ejecución. Cuando pasa la función como argumento a la directiva update-fn="updateFn()", debe incluir el paréntesis (y quizás los parámetros). Pasarlo como referencia de función update-fn="updateFn"no funcionará con el &enlace
JorgeGRC
0

@JorgeGRC Gracias por tu respuesta. Sin embargo, una cosa es que la parte "quizás" es muy importante. Si tiene parámetros, también debe incluirlos en su plantilla y asegurarse de especificar sus locales, por ejemplo updateFn({msg: "Directive Args"}.

usuario2893858
fuente