Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 that should call a directive method but this directive method is not available inside the this controller value.

This is what I have. What I am doing wrong?

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 a codepen with the code.

like image 692
ianaya89 Avatar asked Jan 23 '15 18:01

ianaya89


People also ask

How to call directive method from controller in AngularJS?

Inside the directive it is creating an updateMap() method on the scope object in the directive and then calling the setFn() method which is mapped to the $scope. setDirectiveFn() method by this line: <map set-fn="setDirectiveFn(theDirFn)"></map> in your HTML and this line: scope: { setFn: '&' } in your directive.

What is $parent in AngularJS?

$scope.$parent refers to the $scope of the parent element.

What is attrs in AngularJS?

Using attrs you are able to access the attributes defined in your html tag like <fm-rating ng-model="$parent.restaurant.price" symbol="$" readonly="true"> So in this case you will have access to the symbol and readonly attributes.

How to use custom directive in AngularJS?

AngularJS provides support to create custom directives for following type of elements. Element directives − Directive activates when a matching element is encountered. Attribute − Directive activates when a matching attribute is encountered. CSS − Directive activates when a matching css style is encountered.


2 Answers

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.

like image 178
Pankaj Parkar Avatar answered Sep 23 '22 12:09

Pankaj Parkar


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>
like image 38
BernardV Avatar answered Sep 22 '22 12:09

BernardV