Calling directive's methods from parent controller in AngularJS

后端 未结 5 1099
无人及你
无人及你 2021-02-03 22:59

I am using AngularJS with the alias controllers pattern. I can\'t access (or I don\'t know how to) directive methods from a parent controller.

I have a

相关标签:
5条回答
  • 2021-02-03 23:23

    After trying both the $broadcastand the control object solutions, I would actually recommend trying to bind values or arrays. The control object is a simple way to achieve the desired result, but in my testing very undiscoverable and error-prone.

    This Codepen builds BernardV's example, but uses an array of messages as a very visible control binding. If you want, you can easily $watch the messages array inside the directive as well. The core idea is to in the directive use:

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

    From a controller (having an array of "rooms") you would do this:

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

    Independent directives, independent messages and highly discoverable. You could of course in the directive only show the latest message, or simply bind a string instead of an array. This solution worked much better for us at least.

    0 讨论(0)
  • 2021-02-03 23:28

    You can achieve calling directive methods without relying on $broadcast or removing scope isolation. Similar approaches that have been posted here so far will break if there are 2+ instances of the directive on a page (they'll all reflect the same changes).

    This codepen demonstrates a more robust way to do it.

    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>

    0 讨论(0)
  • 2021-02-03 23:42

    You are isolating the scope when you write:

     scope: {
           text: '='
         },
    

    Here's a slightly modified version of your code, this time, lets you call directive method. Mostly I just got rid of 'scope' in directive, and changed it to using $scope in the controller, rather than this, and Alias pattern..

    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>
    
    0 讨论(0)
  • 2021-02-03 23:46

    I have another solution, that lets you use isolate scope and don't rely on broadcast. In javascript methods can be used like variables, co you can simply pass the method you want to the directive.

    so in html:

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

    and in directive

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

    Here is slightly modyfied codepen, where You can see what i have in mind.

    0 讨论(0)
  • 2021-02-03 23:50

    If you strictly want to use isolated scope inside a directive then the directive method can be only called by using angular events such as $broadcast & $emit

    In your case, you need to use $broadcast to send event to entire $rootScope

    You Code will become like this.

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

    Instead of calling method of child scope, you need to broadcast an event and that will have to be listened by the directive scope & it will fire changeText method after listening to that event.

    NOTE

    Using service / factory would be better approach.

    This would be hopefully help you. Thanks.

    0 讨论(0)
提交回复
热议问题