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?

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


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/


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.


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.


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!


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.


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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.