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.
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.
$scope.$parent refers to the $scope of the parent element.
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.
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.
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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With