Skip to content Skip to sidebar Skip to footer

Calling Directive's Methods From Parent Controller In Angularjs

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 function inside my controller

Solution 1:

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

<divng-app="myApp"><divng-controller="MyCtrl as ctrl"><h1>{{ctrl.text}}</h1><my-dirtext="ctrl.dirText"></my-dir><buttonng-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.

Solution 2:

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) {
    
    functionroom () {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;
          };
        }
      }
    };
  
});
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script><divng-app="myApp"><divng-controller="myChat"><divng-repeat="(roomId, room) in rooms"><divmy-chat-roomroom="{{roomId}}"accessor="room.accessor"></div></div><buttonng-click="addMessages()">Add messages to rooms</button></div></div>

Solution 3:

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-dirtext="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.

Solution 4:

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


<divng-app="myApp"><divng-controller="MyCtrl"><h1>{{text}}</h1><my-dirtext="dirText"></my-dir><buttonng-click="click()">Change Directive Text</button></div></div>

Solution 5:

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.

Post a Comment for "Calling Directive's Methods From Parent Controller In Angularjs"