299 votes

Comment appeler une méthode définie dans une directive AngularJS?

J'ai une directive, voici le code:

 .directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {

            var center = new google.maps.LatLng(50.1, 14.4); 
            $scope.map_options = {
                zoom: 14,
                center: center,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            // create map
            var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
            var dirService= new google.maps.DirectionsService();
            var dirRenderer= new google.maps.DirectionsRenderer()

            var showDirections = function(dirResult, dirStatus) {
                if (dirStatus != google.maps.DirectionsStatus.OK) {
                    alert('Directions failed: ' + dirStatus);
                    return;
                  }
                  // Show directions
                dirRenderer.setMap(map);
                //$scope.dirRenderer.setPanel(Demo.dirContainer);
                dirRenderer.setDirections(dirResult);
            };

            // Watch
            var updateMap = function(){
                dirService.route($scope.dirRequest, showDirections); 
            };    
            $scope.$watch('dirRequest.origin', updateMap);

            google.maps.event.addListener(map, 'zoom_changed', function() {
                $scope.map_options.zoom = map.getZoom();
              });

            dirService.route($scope.dirRequest, showDirections);  
        }
    }
})
 

Je voudrais appeler updateMap () sur une action de l'utilisateur. Le bouton d'action n'est pas sur la directive? Quelle est la meilleure façon de faire ça?

Ho appeler updateMap () dans un contrôleur?

370voto

Oliver Wienand Points 881

Si vous souhaitez utiliser isolé étendues que vous pouvez passer un objet de contrôle de l'utilisation de bi-directionnelle de liaison ('=') d'une variable à partir du contrôleur de portée. De cette façon, vous pouvez aussi le contrôle de plusieurs instances de la même directive sur une page.

J'ai créé un court plunker exemple de référence:

Contrôleur/Directive:

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

app.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {
  };
});

app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link      : function (scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;  
      }
    }
  };
});

HTML:

<button ng-click="focusinControl.takeTablet()">Call directive function</button>
<h4>In controller scope:</h4>
{{focusinControl}}
<h4>In directive scope:</h4>
<focusin control="focusinControl"></focusin>
<h4>Without control object:</h4>
<focusin></focusin>

court plunker exemple

Meilleures salutations,

Oliver Wienand

73voto

Mark Rajcok Points 85912

En supposant que le bouton d'action utilise le même contrôleur $scope que la directive, il suffit de définir la fonction updateMap sur $scope à l'intérieur de la fonction de lien. Votre contrôleur peut ensuite appeler cette fonction lorsque le bouton est cliqué.

<div ng-controller="MyCtrl">
    <map></map>
    <button ng-click="updateMap()">call updateMap()</button>
</div>
app.directive('map', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function($scope, element, attrs) {
            $scope.updateMap = function() {
                alert('inside updateMap()');
            }
        }
    }
});

violon


Comme par @FlorianF commentaire, si la directive est un acte isolé, champ d'application, les choses sont plus complexes. Voici un moyen de le faire fonctionner: ajouter un set-fn d'attribut à l' map directive qui va enregistrer la directive de la fonction avec le contrôleur:

<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
    scope.updateMap = function() {
       alert('inside updateMap()');
    }
    scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
    $scope.setDirectiveFn = function(directiveFn) {
        $scope.directiveFn = directiveFn;
    };
}

violon

35voto

Always Learning Points 203

Bien qu'il puisse être tentant d'exposer un objet isolé champ d'application d'une directive pour faciliter la communication avec elle, cela peut donner lieu à confusion "spaghetti" de code, surtout si vous avez besoin de cette chaîne de communication par l'intermédiaire d'un couple de niveaux (contrôleur, de la directive, imbriquées directive, etc.)

Nous suis allé dans cette voie, mais après un peu plus de recherche a constaté qu'il ne fait plus de sens et entraîné à la fois la plus facile à gérer et lisible le code pour afficher les événements et les propriétés d'une directive à utiliser pour la communication via un service alors à l'aide de $regarder sur ce service, les propriétés de la directive ou de tout autre contrôle qu'il serait nécessaire de réagir à ces changements pour la communication.

Cette abstraction fonctionne très bien avec AngularJS de l'injection de dépendance cadre que vous pouvez injecter le service dans tous les éléments qui ont besoin de réagir à ces événements. Si vous regardez la Angular.js fichier, vous allez voir que les directives de là aussi l'utilisation des services et $regarder de cette manière, ils n'exposent pas des événements isolés portée.

Enfin, dans le cas où vous avez besoin de communiquer entre les directives qui sont dépendants l'un de l'autre, je vous recommande le partage d'un contrôleur entre ces directives que les moyens de communication.

AngularJS du Wiki pour les Meilleures Pratiques mentionne également:

Utilisez uniquement .$diffusion(), .$emit() et .$sur() pour les événements atomiques Les événements qui sont pertinentes à l'échelle mondiale à travers l'ensemble de l'application (par exemple, un utilisateur de l'authentification ou l'application de clôture). Si vous voulez des événements spécifiques à des modules, des services ou des widgets, vous devriez envisager de Services, la Directive Contrôleurs, ou 3ème Partie Libs

  • $scope.$d'une montre() devrait remplacer la nécessité pour les événements
  • L'injection de services et de l'appel de méthodes directement les est également utile pour la communication directe
  • Les Directives sont en mesure de communiquer directement les uns avec les autres au travers de la directive-contrôleurs

15voto

CheapSteaks Points 629

Bâtiment sur Oliver répondez - vous peut-être pas toujours besoin d'accéder à une directive interne des méthodes, et dans ces cas, vous probablement ne voulez pas avoir à créer un objet vide et ajouter un control attr à la directive juste pour l'empêcher de lancer une erreur (cannot set property 'takeTablet' of undefined).

Vous pouvez également utiliser la méthode dans d'autres lieux dans la directive.

Je tiens à ajouter assurez - scope.control existe, et de définir les méthodes dans un mode similaire à la révélation motif de module

app.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{control}}</div>',
    scope: {
      control: '='
    },
    link : function (scope, element, attrs) {
      var takenTablets = 0;
      var takeTablet = function() {
        takenTablets += 1;  
      }

      if (scope.control) {
        scope.control = {
          takeTablet: takeTablet
        };
      }
    }
  };
});

5voto

threed Points 2131

Vous pouvez spécifier un DOM attribut qui peut être utilisé pour permettre la directive pour définir une fonction sur la portée parent. Le parent peut alors appeler cette méthode comme une autre. Voici un plunker. Et ci-dessous est le code pertinent.

clearfn est un attribut sur la directive de l'élément dans lequel le parent peut passer d'un champ d'application bien que la directive peut alors définir une fonction qui accomplissent le comportement souhaité.

<!DOCTYPE html>
<html ng-app="myapp">
  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <style>
      my-box{
        display:block;
        border:solid 1px #aaa;
        min-width:50px;
        min-height:50px;
        padding:.5em;
        margin:1em;
        outline:0px;
        box-shadow:inset 0px 0px .4em #aaa;
      }
    </style>
  </head>
  <body ng-controller="mycontroller">
    <h1>Call method on directive</h1>
    <button ng-click="clear()">Clear</button>
    <my-box clearfn="clear" contentEditable=true></my-box>
    <script>
      var app = angular.module('myapp', []);
      app.controller('mycontroller', function($scope){
      });
      app.directive('myBox', function(){
        return {
          restrict: 'E',
          scope: {
            clearFn: '=clearfn'
          },
          template: '',
          link: function(scope, element, attrs){
            element.html('Hello World!');
            scope.clearFn = function(){
              element.html('');
            };
          }
        }
      });
    </script>
  </body>
</html>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X