AngularJS communication between directives

22,822

Solution 1

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

Solution 2

From your example the directive structure is not parent-child. Therefore you can't share methods through their controllers. I would use $rootScope.$broadcast. (See DOCS)

One directive calls:

$rootScope.$broadcast('someEvent', [1,2,3]);

Second directive listens:

 scope.$on('someEvent', function(event, mass) {
    console.log(mass)}
  );

Demo Fiddle


Fixed directives:

app.directive("firstDir", function ($rootScope) {
    return {
        restrict: 'E',
        link: function (scope, element, attrs) {
            scope.dataToPass = 'empty';
            scope.doClick = function (valueToPass) {
                scope.dataToPass = valueToPass;
                $rootScope.$broadcast('someEvent', {
                    data: valueToPass
                });
            }
        }
    };
});

app.directive("secondDir", function () {
    return {
        restrict: 'E',
        link: function (scope, element, attrs) {
            scope.receivedData = 'none';

            scope.$on('someEvent', function (event, result) {
                scope.receivedData = result.data;
            });
        }
    }
});
Share:
22,822

Related videos on Youtube

tibbus
Author by

tibbus

Full stack Developer

Updated on July 09, 2022

Comments

  • tibbus
    tibbus almost 2 years

    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'
            };  
        });
    })();
    
  • akn
    akn almost 10 years
    broadcast is better because it sends event downwards. Btw, I think that in firstDir, $rootScope should be injected as an directive argument, not as argument in controller function
  • PSL
    PSL almost 10 years
    @akn It does not matter, you can inject it in controller as well. Also in this case you dont even need to use $rootScope $scope.$emit is better in terms of performance.
  • whyAto8
    whyAto8 over 9 years
    Thanks for this, I used broadcast in my case, as emit didnt work. My 2 directives (i.e the 2 elements on which they are attached) are directly appended to body and the broadcast is called from deep inside of one of the element. So, there is no parent/child relation at all. But as per explanation, broadcast sends event downwards, so how is it working. Initially I expected emit to work, but it didnt. Could you throw some light please.
  • Sebastian Wozny
    Sebastian Wozny over 5 years
    This is really bad practice because it requires the children to know about their parents and will make your application a highly entangled mess.