web-dev-qa-db-fra.com

Appeler les méthodes de la directive depuis le contrôleur parent dans AngularJS

J'utilise AngularJS avec le motif de contrôleur d'alias. Je ne peux pas accéder (ou je ne sais pas comment) à des méthodes de directive à partir d'un contrôleur parent. 

J'ai une fonction dans mon contrôleur qui devrait appeler une méthode de directive, mais cette méthode de directive n'est pas disponible dans la valeur du contrôleur this.

C'est ce que j'ai. Qu'est ce que je fais mal?

JS

angular.module('myApp', []).

controller('MyCtrl', function(){
  this.text = 'Controller text';

  this.dirText = 'Directive text';

  this.click = function(){
    this.changeText();
  }
})

.directive('myDir', function(){
  return {
     restrict: 'E',
     scope: {
       text: '='
     },
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
     },
     template: '<h2>{{text}}</h2>'
  };
});

HTML

<div ng-app="myApp">
  <div ng-controller="MyCtrl as ctrl">
    <h1>{{ctrl.text}}</h1>
    <my-dir text="ctrl.dirText"></my-dir>
    <button ng-click="ctrl.click()">Change Directive Text</button>
  </div>
</div>

Ici un codepen avec le code.

25
ianaya89

Si vous voulez strictement utiliser la variable scope isolée dans une directive, la méthode de la directive ne peut être appelée qu'à l'aide d'événements angulaires tels que $broadcast & $emit.

Dans votre cas, vous devez utiliser $broadcast pour envoyer un événement à $rootScope entier.

Votre code deviendra comme ça.

Stylo à code de travail

HTML

<div ng-app="myApp">
  <div ng-controller="MyCtrl as ctrl">
    <h1>{{ctrl.text}}</h1>
    <my-dir text="ctrl.dirText"></my-dir>
    <button ng-click="ctrl.click()">Change Directive Text</button>
  </div>
</div>

CODE

angular.module('myApp', []).

controller('MyCtrl', function($rootScope){
  var that = this;

  this.text = 'Controller text';

  this.dirText = 'Directive text';

  this.click = function(){
      $rootScope.$broadcast('changeText',{});
  }
}).

directive('myDir', function(){
  return {
     restrict: 'E',
     scope: {
       text: '='
     },
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
         scope.$on('changeText',function(event, data){
             scope.changeText()
         });
     },
     template: '<h2>{{text}}</h2>'
  };
});

Au lieu d'appeler la méthode d'envergure enfant, vous devez diffuser un événement qui devra être écouté par la directive scope. La méthode changeText sera déclenchée après l'écoute de cet événement.

REMARQUE

Utiliser le service/usine serait une meilleure approche.

J'espère que cela vous aiderait. Merci.

29
Pankaj Parkar

Vous pouvez utiliser des méthodes de directive d'appel sans recourir à $ broadcast ni supprimer l'isolation de la portée. Des approches similaires qui ont été publiées ici jusqu’à présent se briseront s’il ya plus de 2 instances de la directive sur une page (elles refléteront toutes les mêmes changements).

Ce codepen illustre une méthode plus robuste pour le faire.

angular.module('myApp', [])
.controller('myChat', function($scope) {
    
    function room () {return { accessor:{} }; }
    $scope.rooms = { 'RoomA': new room, 'RoomB': new room, 'RoomC': new room };

    $scope.addMessageTo = function(roomId, msg) {
      if ($scope.rooms[roomId].accessor.setMessage)
        $scope.rooms[roomId].accessor.setMessage(msg);
    };

    $scope.addMessages = function () {
      $scope.addMessageTo("RoomA", "A message");
      $scope.addMessageTo("RoomB", "Two messages");
      $scope.addMessageTo("RoomC", "More messages");
    }
    
}).directive('myChatRoom', function() {

    return {
      template: '<div>{{room}} message = {{message}}<div />',
      scope: { accessor: "=", room: "@" },
      link: function (scope) {
        if (scope.accessor) {
          scope.accessor.setMessage = function(msg) {
            scope.message = msg;
          };
        }
      }
    };
  
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="myChat">

    <div ng-repeat="(roomId, room) in rooms">
      <div my-chat-room room="{{roomId}}" accessor="room.accessor"></div>
    </div>

    <button ng-click="addMessages()">Add messages to rooms</button>

  </div>
</div>

16
BernardV

J'ai une autre solution, qui vous permet d'utiliser isoler la portée et de ne pas compter sur la diffusion. En javascript, les méthodes peuvent être utilisées comme variables, mais vous pouvez simplement passer la méthode de votre choix à la directive. 

donc en html:

<my-dir text="ctrl.dirText" change-text="ctrl.changeText"></my-dir>

et en directive

scope: {
   text: '=',
   changeText: '='
 }

Ici est un codepen légèrement modifié, où vous pouvez voir ce que j'ai en tête.

11
Paweł Dziubałka

Vous isolez la portée lorsque vous écrivez:

 scope: {
       text: '='
     },

Voici une version légèrement modifiée de votre code, cette fois-ci, vous permet d’appeler la méthode de la directive. La plupart du temps, je viens de me débarrasser de 'scope' dans la directive et de le changer pour utiliser $ scope dans le contrôleur, plutôt que cela et le motif Alias ​​.. 

AVERTISSEMENT: cela pourrait ne pas refléter le comportement correct, en ce qui concerne quelles variables sont modifiées, mais répond à votre question en montrant comment vous pouvez accéder à la méthode de la directive à partir du contrôleur. Ce n'est généralement pas une bonne idée de design ..

http://codepen.io/anon/pen/azwJBm

angular.module('myApp', []).

controller('MyCtrl', function($scope){
  var that = this;

  $scope.text = 'Controller text';

  $scope.dirText = 'Directive text';

  $scope.click = function(){
    $scope.changeText();
  }
}).

directive('myDir', function(){
  return {
     restrict: 'E',
    /* scope: {
       text: '='
     },*/
     link: function(scope, element, attrs){
       scope.changeText = function(){
         scope.text = 'New directive text';
       };
     },
     template: '<h2>{{text}}</h2>'
  };
});


<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <h1>{{text}}</h1>
    <my-dir text="dirText"></my-dir>
    <button ng-click="click()">Change Directive Text</button>
  </div>
</div>
2
Shaunak

Après avoir essayé à la fois les solutions d'objet $broadcast et control, je vous recommanderais d'essayer de lier des valeurs ou des tableaux. L’objet control est un moyen simple d’atteindre le résultat souhaité, mais dans mes tests, il est très difficile de le retrouver et de le rendre vulnérable aux erreurs.

Ce Codepen construit BernardV exemple, mais utilise un tableau de messages comme une liaison de contrôle très visible. Si vous le souhaitez, vous pouvez également $watch facilement afficher le tableau de messages dans la directive. L'idée centrale est d'utiliser dans la directive:

scope: { messages: "=", room: "@" },

Depuis un contrôleur (ayant un tableau de "pièces"), vous feriez ceci:

$scope.addMessages = function () {
  angular.forEach($scope.rooms, function(room, index) {
    room.messages.Push("A new message! # " + (index+1);
  })
} 

Directives indépendantes, messages indépendants et hautement découvrables. Vous pouvez bien sûr dans la directive afficher uniquement le dernier message ou simplement lier une chaîne au lieu d'un tableau. Cette solution a beaucoup mieux fonctionné pour nous au moins.

0
Victor