I'am new to Angular.js, I need for my application some communication between directives, I read some documentation about link and require, but can't understand exactly how it works.
For a simple example I have : live fiddle : http://jsfiddle.net/yw235n98/5/
HTML :
<body ng-app="myApp"> First Directive : <first-dir > <h3>{{firstCtrl.data}}</h3> <button ng-click="firstCtrl.set('NEW VALUE')">Change Value</button> </first-dir> Second Directive : <second-dir> <h3>{{secondCtrl.data}}</h3> </second-dir>
Javascript :
(function(){ var app = angular.module('myApp', []); app.directive("firstDir", function(){ return { restrict : 'E', controller : function(){ this.data = 'init value'; this.set = function(value){ this.data = value; // communication with second Directive ??? } }, controllerAs : 'firstCtrl' }; }); app.directive("secondDir", function(){ return { restrict : 'E', controller : function(){ this.data = 'init value'; }, controllerAs : 'secondCtrl' }; }); })();
The best way to pass an object to an angular directive is by using the &. When you use &, angular compiles the string as an expression and sets the scope variable in your directive to a function that, when called, will evaluate the expression in the context of the directive's parent's scope.
$scope.$parent refers to the $scope of the parent element.
... is quite illustrative as AngularJS doesn't allow multiple directives (on the same DOM level) to create their own isolate scopes. According to the documentation, this restriction is imposed in order to prevent collision or unsupported configuration of the $scope objects.
AngularJS directives are extended HTML attributes with the prefix ng- . The ng-app directive initializes an AngularJS application. The ng-init directive initializes application data. The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.
One way you can communicate between them using what is called eventing.
One directive can emit an event on the rootscope which can then be listened by anybody who wants to. You could use $rootScope.$emit
or $rootScope.$broadcast
to publish events with data and use $scope.$on
to listen to the event. In your case you could just do $scope.$emit
as well.
app.directive("firstDir", function(){ return { restrict : 'E', controller : function($scope){ this.data = 'init value'; this.set = function(value){ //EMIT THE EVENT WITH DATA $scope.$emit('FIRST_DIR_UPDATED', value); this.data = value; // communication with second Directive ??? } }, controllerAs : 'firstCtrl' }; }); app.directive("secondDir", function(){ return { restrict : 'E', controller : function($scope){ var _that = this; //LISTEN TO THE EVENT $scope.$on('FIRST_DIR_UPDATED', function(e, data){ _that.data = data; }); this.data = 'init value'; }, controllerAs : 'secondCtrl' }; });
Demo Demo2
____________________________________________________________________________
Now speaking of which, it sometimes is really required to inject $rootScope
just to have the eventing enabled to a different node in your application. You can instead have a pub/sub mechanism easily built in your app and make use of prototypical inheritance.
Here i am adding 2 methods publish
and subscribe
on $rootScope's
prototype during app initialization. So any child scope or isolated scope will have these methods available and communication will be so easier without worrying about whether to use $emit
, $broadcast
, whether i need to inject a $rootscope
for communication from isolated scoped directive etc.
app.service('PubSubService', function () { return {Initialize:Initialize}; function Initialize (scope) { //Keep a dictionary to store the events and its subscriptions var publishEventMap = {}; //Register publish events scope.constructor.prototype.publish = scope.constructor.prototype.publish || function () { var _thisScope = this, handlers, args, evnt; //Get event and rest of the data args = [].slice.call(arguments); evnt = args.splice(0, 1); //Loop though each handlerMap and invoke the handler angular.forEach((publishEventMap[evnt] || []), function (handlerMap) { handlerMap.handler.apply(_thisScope, args); }) } //Register Subscribe events scope.constructor.prototype.subscribe = scope.constructor.prototype.subscribe || function (evnt, handler) { var _thisScope = this, handlers = (publishEventMap[evnt] = publishEventMap[evnt] || []); //Just keep the scopeid for reference later for cleanup handlers.push({ $id: _thisScope.$id, handler: handler }); //When scope is destroy remove the handlers that it has subscribed. _thisScope.$on('$destroy', function () { for(var i=0,l=handlers.length; i<l; i++){ if (handlers[i].$id === _thisScope.$id) { handlers.splice(i, 1); break; } } }); } } }).run(function ($rootScope, PubSubService) { PubSubService.Initialize($rootScope); });
and you could just have any place from your app publish an event without requiring a rootScope.
$scope.publish('eventName', data);
and listen anywhere on the application without worrying about using $rootScope
or $emit
or $broadcast
:-
$scope.subscribe('eventName', function(data){ //do somthing });
Demo - PubSub
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