Связь с Angularjs между директивами с публикацией/подпиской

Я хочу создать механизм публикации/подписки с системой событий angular.

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);
            });
      }
    }
})

если я устанавливаю HTML-документ таким образом, в консоли не появляется сообщение:

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

Если я меняю порядок первым и вторым, на сервере будет отображаться сообщение wirite.

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

Но мне нужна первая директива или внутренняя директива.

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

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

Я пробовал как $rootScope.$broadcast, так и $rootScope.$emit, но не смотрел.

Рабочий DEMO

Ответ 1

Это абсолютно правильное поведение angular.

В первом примере:

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

Angular создает директиву для тега first и отправляет событие немедленно, но вторая директива еще не создана.

Во втором примере:

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

Здесь вы сначала подписываетесь на событие, а после этого директива first отправляет сообщение. Из-за этого директива second принимает событие.

Третий пример:

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

Этот случай, как и первый пример, не будет работать.

Решение: Одним из решений является установка тайм-аута в первой директиве, чтобы не отправлять событие сразу после создания. Если второй параметр $timeout, delay не указан, поведение по умолчанию заключается в выполнении функции после завершения рендеринга DOM:

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"
        })
      });
    }
  }
});

Демо

Ответ 2

Это происходит потому, что, когда выполняется трансляция первой директивы, вторая директива не готова и, следовательно, сигнал теряется и связь не происходит. Один из способов его решения - многократно посылать сигнал с использованием функции $interval и прекращать передачу только тогда, когда вторая директива готова. Конечно, вторая директива должна транслировать назад, когда она получает данные от первого. Второе решение, для которого я должен был пойти, заключается в том, что вторая директива уведомляет, когда она готова к первой директиве, транслируя с помощью $rootScope так же, как в первой директиве.

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"
        });
      }
    }
 })

и первая директива:

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);
           });
      }
   }
})

Поскольку вы используете родительскую дочернюю структуру, всегда гарантируется, что первая директива будет готова, когда дочерние директивы будут готовы. Используя это решение, вы можете использовать многие дочерние директивы. Надеюсь, это поможет!