Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs event communication between directives with publish/subscribe

I want to create a publish/subscribe mechanism with angular event system.

angular.module("app",[]);

angular.module("app").directive("first", function($rootScope){
        return {
          template: "First Directive",
          link:function(scope,element,attribute){
               $rootScope.$broadcast("OnFirstDirectiveCreated", {
                "message": "I'm first directive"
            });
      }
    }
})

angular.module("app").directive("second", function($rootScope){
        return {
          template: "Second Directive",
          link:function(scope,element,attribute){
            var handler = $rootScope.$on("OnFirstDirectiveCreated", 
                function (event, args) {
                  console.log("First Directive Message: " + args.message);
            });
      }
    }
})

if I set HTML document like this, no message appear in console:

<div ng-app="app">
  <first></first>
  <second></second>
</div>

If I change order first and second, message wirite on console.

<div ng-app="app">
  <second></second>
  <first></first>
</div>

But I need first directive or inner directive.

<div ng-app="app">
  <first></first>
  <second></second>
</div>

<div ng-app="app">
  <first>
      <second></second>
  </first>
</div>

I tried both $rootScope.$broadcast and $rootScope.$emit but did not woeked.

Working DEMO

like image 346
barteloma Avatar asked May 05 '16 08:05

barteloma


2 Answers

It's absolutely correct angular behavior.

In the first example:

<first></first>
<second></second>

Angular creates a directive for first tag and sends event immediately, but the second directive is not created yet.

In the second example:

<first></first>
<second></second>

Here you firstly subscribe to an event, and after that first directive sends a message. Because of this, second directive accepts an event.

Third example:

<first>
   <second></second>
</first>

This case, as well as the first example won't be working.

Solution: One of solutions is to set timeout in first directive to don't send event immediately after creation. If the second parameter of $timeout, delay is not provided, the default behaviour is to execute the function after the DOM has completed rendering:

angular.module("app").directive("first", function($rootScope, $timeout) {
  return {
    template: "First Directive",
    link: function(scope,element,attribute) {
      $timeout(function() {
        $rootScope.$broadcast("OnFirstDirectiveCreated", {
          "message": "I'm first directive"
        })
      });
    }
  }
});

Demo

like image 141
alexmac Avatar answered Oct 07 '22 00:10

alexmac


This happens because when broadcast of first directive is executed, the second directive is not ready and therefore the signal is lost and communication does not happen. One way of solving it, is to send the signal many times using $interval function and stop transmitting only when the second directive is ready. Of course the second directive has to broadcast back when it receives data from the first. The second solution, for which I would go, is for the second directive to notify when its ready to the first directive by broadcasting with $rootScope in a similar fashion as you did in first directive.

angular.module("app").directive("second", function($rootScope){
    return {
      template: "Second Directive",
      link:function(scope,element,attribute){
        var handler = $rootScope.$on("secondDirective", 
            function (event, args) {
              console.log("First Directive Data: " + args);
        });

        $rootScope.$broadcast("OnChildDirectiveCreated", {
            "message": "I'm second directive",
            "id":      "secondDirective"
        });
      }
    }
 })

and the first directive:

angular.module("app").directive("first", function($rootScope){
    return {
      template: "First Directive",
      link:function(scope,element,attribute){
           $rootScope.$on("OnChildDirectiveCreated", function(event, args) {
              $rootScope.$broadcast(args.id, someData);
           });
      }
   }
})

Since you are using a parent child structure, it is always guaranteed that the first directive will be ready when child directives are ready. Using this solution you could use many child directives. Hope this helps!

like image 43
Spartak Lalaj Avatar answered Oct 07 '22 00:10

Spartak Lalaj