Question
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/
- 2 directives : firstDir, secondDir :: with some data
- firstDir have a click function that will change the data value
- when firsDir click function is triggered I want to change data in secondDir too.
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'
};
});
})();
Answer
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'
};
});
____________________________________________________________________________
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
});