AngularJS - passa a função para diretiva

160

Eu tenho um exemplo 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>

Desejo que, quando clicar no botão, a caixa de alerta será exibida, mas nada será exibido.

Alguém pode me ajudar?

user2707026
fonte

Respostas:

243

Para chamar uma função de controlador no escopo pai de dentro de uma diretiva de escopo isolado, use dash-separatednomes de atributos no HTML, como o OP disse.

Além disso, se você deseja enviar um parâmetro para sua função, chame a função passando um 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
fonte
1
Agradeço ao Codezilla pela sua resposta e quero perguntar sobre a circunstância em que quero vincular a função "updateFn" do escopo pai para isolar o escopo na diretiva "teste", isso é possível?
user2707026
2
O replaceatributo foi descontinuado no AngularJS: stackoverflow.com/questions/24194972/…
cdmckay
8
por alguma razão, o argumento é indefinido para mim.
chovy 9/03/2015
1
@ Chovy Acho que o argumento é usado apenas quando você chama o método novamente? O primeiro uso do suporte aberto parece ser o formato que as necessidades angulares para o método apenas para ser passado, mas eu poderia ser errado lá
marksyzm
1
Um mapeamento de objeto updateFn({msg: 'my message'});deve ser usado nesse formato ao fazer a chamada de função dentro da linkfunção da diretiva .
Brian
159

Talvez esteja faltando alguma coisa, mas, embora as outras soluções chamem a função de escopo pai, não há capacidade de passar argumentos do código de diretiva, isso ocorre porque a update-fnchamada é feita updateFn()com parâmetros fixos, por exemplo {msg: "Hello World"}. Uma pequena mudança permite que a diretiva passe argumentos, o que eu consideraria muito mais útil.

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

Observe que o HTML está passando uma referência de função, ou seja, sem ()colchetes.

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

Portanto, acima, o HTML está chamando a callUpdatefunção de escopo local , que 'busca' o updateFn do escopo pai e chama a função retornada com parâmetros que a diretiva pode gerar.

http://jsfiddle.net/mygknek2/

Steve
fonte
9
Não sabe como posso obter um voto negativo para algo que funciona? Você deve deixar um comentário se quiser votar abaixo.
Steve
7
Isso funcionou para mim. Se você não quiser que a função extra apenas escreverng-click="updateFn()('Directive Args')"
Graham Walters
7
Awwww! scope.updateFn () ("Diretivas da diretiva"); !! NOT scope.updateFn ("Diretivas da diretiva"); !!!
Phung D. Um
2
Esta é a resposta mais perfeita mesmo !!
Vinesh
11
@ Ludwik11 com certeza - é porque scope.updateFn quando definida como essa é uma função que retorna uma função (daí a () ()) e isso é porque passamos para o escopo (via update-fn = "updateFn" em html) uma referência para a função que queremos chamar. O 1º () é uma chamada para angular para retornar essa referência, o 2º () faz a chamada para nossa função e é onde passamos quaisquer parâmetros. HTH
steve
39

Na tag Html da diretiva 'test', o nome do atributo da função não deve ser camelCased, mas baseado em traços.

então - em vez de:

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

escrever:

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

Esta é a maneira angular de dizer a diferença entre os atributos da diretiva (como a função update-fn) e as funções.

Ofer Segev
fonte
1
obrigado pela captura. Eu incluí isso na minha resposta. Votado! :)
AlwaysALearner
10

Que tal passar a função do controlador com ligação bidirecional ? Em seguida, você pode usá-lo na diretiva exatamente da mesma maneira que em um modelo regular (retirei partes irrelevantes por simplicidade):

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

Cheguei a essa pergunta, porque tentei o método acima antes, mas de alguma forma não funcionou. Agora funciona perfeitamente.

Márton Tamás
fonte
5

use traço e letras minúsculas para o nome do atributo (como outras respostas disseram):

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

E use "=" em vez de "&" no escopo da diretiva:

 scope: { updateFn: '='}

Então você pode usar o updateFn como qualquer outra função:

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

Ai está!

Jane
fonte
5
Por que você usaria '=' em vez de '&'? quando tentei, ele continuou chamando repetidamente minha função.
precisa saber é o seguinte
2
É errado usar '=' para isso. Isso é para ligação de objetos bidirecional.
Ben Taliadoros
1
Eu acho que o único problema é o uso de parênteses no primeiro modelo. Isso executa a função e vincula o resultado. Em vez disso você deve passar apenas o nome da função, assim:update-fn="updateFn"
Márton Tamás
1
Resposta ruim. muito mal.
towry
4

Eu tive que usar a ligação "=" em vez de "&" porque isso não estava funcionando. Comportamento estranho.

Gunter Reinitzer
fonte
2
Isso ocorre porque você provavelmente está passando a diretiva como referência de função JS em vez da execução. Quando você passa a função como argumento para a diretiva, update-fn="updateFn()"deve incluir os parênteses (e talvez os parâmetros). Passando-o como uma função de referência update-fn="updateFn"não vai funcionar com a &ligação
JorgeGRC
0

@JorgeGRC Obrigado pela sua resposta. Uma coisa, no entanto, a parte "talvez" é muito importante. Se você possui parâmetros, também deve incluí-los no seu modelo e especificar seus locais, por exemplo updateFn({msg: "Directive Args"}.

user2893858
fonte