在 AngularJS 中从父控制器调用指令的方法

Calling directive's methods from parent controller in 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>

Here 带有代码的代码笔。

您在编写时隔离范围:

 scope: {
       text: '='
     },

这里是您的代码的一个稍微修改过的版本,这次,您可以调用指令方法。大多数情况下,我只是去掉了指令中的 'scope',并将其更改为在控制器中使用 $scope,而不是这个和 Alias 模式。

WARNING: This might not reflect the correct behavior, with regard's to which variables get changed, but answers your question by showing how you can access directive's method from controller. This is usually not a good design idea..

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>

如果您确实想在指令中使用隔离的 scope,则只能通过使用 angular 事件(例如 $broadcast$emit[ 来调用指令方法=20=]

在您的情况下,您需要使用 $broadcast 将事件发送到整个 $rootScope

你的代码会变成这样

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>

代码

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 方法。

注意

Using service / factory would be better approach.

希望对您有所帮助。谢谢。

我有另一种解决方案,可以让您使用隔离作用域而不依赖于广播。在 javascript 中,方法可以像变量一样使用,你可以简单地将你想要的方法传递给指令。

因此 html:

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

并在指令中

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

Here 是稍微修改过的代码笔,您可以在其中看到我的想法。

您可以在不依赖 $broadcast 或移除范围隔离的情况下实现调用指令方法。如果页面上有 2 个以上的指令实例(它们都将反映相同的更改),那么到目前为止已在此处发布的类似方法将会失效。

This codepen 演示了一种更可靠的方法。

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>

在尝试了 $broadcastcontrol 对象解决方案之后,我实际上建议尝试绑定值或数组。 control 对象是实现预期结果的简单方法,但在我的测试中非常难以发现且容易出错。

This Codepen builds 的示例,但使用消息数组作为非常明显的控件绑定。如果需要,您也可以轻松 $watch 指令中的消息数组。核心思想是在指令中使用:

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

从一个控制器(有一个 "rooms" 的数组)你会这样做:

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

独立的指令、独立的消息和高度可发现的。你当然可以在指令中只显示最新的消息,或者简单地绑定一个字符串而不是一个数组。这个解决方案至少对我们来说效果更好。