it-swarm.com.de

Aufrufen der Direktive-Methoden vom übergeordneten Controller in AngularJS

Ich verwende AngularJS mit dem Alias-Controller-Muster. Ich kann nicht auf Direktive-Methoden von einem übergeordneten Controller aus zugreifen (oder ich weiß nicht, wie). 

Ich habe eine Funktion in meinem Controller, die eine Direktive-Methode aufrufen sollte, diese Direktive-Methode ist jedoch nicht im Controller-Wert this verfügbar.

Das ist was ich habe. Was mache ich falsch?

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>

Hier ein Codeepen mit dem Code.

25
ianaya89

Wenn Sie strikt nur scope in einer Direktive verwenden möchten, kann die Direktionsmethode nur mithilfe von Winkelereignissen wie $broadcast & $emit aufgerufen werden.

In Ihrem Fall müssen Sie $broadcast verwenden, um das Ereignis an den gesamten $rootScope zu senden.

Ihr Code wird so werden.

Working Code Pen

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

Anstatt die Methode des untergeordneten Bereichs aufzurufen, müssen Sie ein Ereignis übertragen, das von der Richtlinie der Richtlinie abgehört werden muss. Nach dem Abhören des Ereignisses wird die changeText-Methode ausgelöst.

HINWEIS

Die Verwendung von Service/Fabrik wäre ein besserer Ansatz.

Das würde Ihnen hoffentlich helfen. Vielen Dank.

29
Pankaj Parkar

Sie können aufrufende Direktive-Methoden erreichen, ohne sich auf $ broadcast zu verlassen oder die Isolation des Bereichs zu entfernen. Ähnliche Ansätze, die bisher hier veröffentlicht wurden, werden brechen, wenn sich auf einer Seite mehr als zwei Instanzen der Direktive befinden (sie alle spiegeln die gleichen Änderungen wider).

Dieses Codepen demonstriert eine robustere Methode.

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

Ich habe eine andere Lösung, mit der Sie den Isolatebereich verwenden können und nicht auf Broadcast angewiesen sind. In Javascript können Methoden wie Variablen verwendet werden. Sie können einfach die gewünschte Methode an die Direktive übergeben. 

so in html:

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

und in der Direktive

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

Hier ist etwas modifiziertes Codepen, wo man sehen kann was ich mir vorstelle.

11

Sie isolieren den Bereich, wenn Sie schreiben:

 scope: {
       text: '='
     },

Hier ist eine etwas modifizierte Version Ihres Codes. Diesmal können Sie die Direktive-Methode aufrufen. Meistens habe ich gerade 'scope' in der Direktive entfernt und es geändert, um $ scope im Controller zu verwenden, anstatt dieses und Alias-Muster. 

WARNUNG: Dies spiegelt möglicherweise nicht das richtige Verhalten in Bezug auf .__ wider. Welche Variablen werden geändert, beantwortet aber Ihre Frage, indem Sie zeigen, wie Sie können über die Steuerung auf die Direktive-Methode zugreifen. Dies ist normalerweise nicht eine gute designidee ..

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

Nachdem Sie sowohl die $broadcast- als auch die control-Objektlösung ausprobiert haben, würde ich tatsächlich empfehlen, Werte oder Arrays zu binden. Das control-Objekt ist ein einfacher Weg, um das gewünschte Ergebnis zu erzielen, ist aber in meinen Tests sehr unerkennbar und fehleranfällig.

Dieses Codepen builds BernardV s Beispiel, verwendet jedoch ein Array von Nachrichten als sehr sichtbare Steuerelementbindung. Wenn Sie möchten, können Sie auch das Nachrichten-Array in der Direktive problemlos $watch eingeben. Die Kernidee ist, in der Richtlinie zu verwenden:

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

Von einem Controller (mit einem Array von "Räumen") würden Sie Folgendes tun:

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

Unabhängige Anweisungen, unabhängige Nachrichten und sehr gut auffindbar. Sie können natürlich in der Direktive nur die neueste Nachricht anzeigen oder einfach eine Zeichenfolge anstelle eines Arrays binden. Diese Lösung hat zumindest für uns viel besser funktioniert.

0
Victor