it-roy-ru.com

Вызов методов директивы из родительского контроллера в AngularJS

Я использую AngularJS с шаблоном псевдонимов контроллеров. Я не могу получить доступ (или не знаю как) к директивным методам из родительского контроллера. 

У меня внутри контроллера есть функция, которая должна вызывать метод директивы, но этот метод директивы недоступен внутри значения контроллера this.

Это то, что у меня есть. Что я делаю не так?

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>

Здесь кодовая ручка с кодом.

25
ianaya89

Если вы строго хотите использовать изолированный scope внутри директивы, тогда метод директивы может быть вызван только с использованием угловых событий, таких как $broadcast & $emit

В вашем случае вам нужно использовать $broadcast для отправки события на весь $rootScope

Ваш код станет таким.

Рабочий код 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>'
  };
});

Вместо того, чтобы вызывать метод дочерней области, вам нужно передать событие, которое будет прослушиваться директивой области действия, и после прослушивания этого события будет запущен метод changeText.

НОТА

Использование сервиса/фабрики было бы лучшим подходом.

Надеюсь, это поможет вам. Благодарю.

29
Pankaj Parkar

Вы можете добиться вызова методов директивы, не полагаясь на $ broadcast или не удаляя изоляцию области видимости. Подобные подходы, которые были опубликованы здесь до сих пор, сломаются, если на странице будет более 2 экземпляров директивы (все они будут отражать одинаковые изменения).

Этот кодовый блок демонстрирует более надежный способ сделать это.

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

У меня есть другое решение, которое позволяет вам использовать изолированную область и не полагаться на трансляцию. В javascript методы могут использоваться как переменные, так что вы можете просто передать нужный метод в директиву. 

так в html:

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

и в директиве

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

Здесь это слегка модифицированный кодовый блок, где вы можете увидеть, что я имею в виду.

11
Paweł Dziubałka

Вы изолируете область видимости, когда пишете:

 scope: {
       text: '='
     },

Вот немного измененная версия вашего кода, на этот раз позволяет вызывать метод директивы. В основном я просто избавился от 'scope' в директиве и изменил его на использование $ scope в контроллере, а не this, и шаблона Alias. 

ВНИМАНИЕ: Это может не отражать правильное поведение, в отношении какие переменные меняются, но отвечает на ваш вопрос, показывая, как Вы можете получить доступ к методу директивы из контроллера. Обычно это не хорошая идея дизайна ..

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

Попробовав оба решения $broadcast и control, я бы порекомендовал попробовать связать значения или массивы. Объект control - это простой способ достижения желаемого результата, но в моем тестировании он очень не обнаруживаем и подвержен ошибкам.

Этот Codepen builds пример BernardV , но использует массив сообщений в качестве очень видимой привязки элемента управления. Если вы хотите, вы можете легко $watch массив сообщений внутри директивы. Основная идея заключается в использовании директивы:

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

Из контроллера (имеющего массив «комнат») вы могли бы сделать это:

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

Независимые директивы, независимые сообщения и очень открыты. Конечно, в директиве вы можете показать только последнее сообщение или просто связать строку вместо массива. Это решение работало намного лучше для нас, по крайней мере.

0
Victor