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

时间:2015-01-23 18:46:50

标签: javascript angularjs events angularjs-directive angularjs-controller

我正在使用 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带有代码的codepen。

5 个答案:

答案 0 :(得分:29)

如果您严格要在指令中使用隔离的scope,那么只能使用角度事件调用指令方法,例如$broadcast&amp; $emit

在您的情况下,您需要使用$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>

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

不需要调用子范围的方法,而是需要广播一个事件,并且必须由指令范围&amp;听完那个事件后它会触发changeText方法。

注意

  

使用服务/工厂将是更好的方法。

这对您有所帮助。感谢。

答案 1 :(得分:16)

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

This codepen演示了一种更强大的方法。

&#13;
&#13;
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;
          };
        }
      }
    };
  
});
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:11)

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

所以在html:

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

并在指令

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

Here稍微改变了代码,你可以在那里看到我的想法。

答案 3 :(得分:2)

您在撰写时正在隔离范围:

 scope: {
       text: '='
     },

这是代码的略微修改版本,这一次,让您调用指令方法。大多数情况下,我只是在指令中删除'scope',并将其更改为在控制器中使用$ scope,而不是这个,以及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>

答案 4 :(得分:0)

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

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

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

从一个控制器(有一组&#34;房间&#34;)你可以这样做:

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

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