Пользовательская директива Angular, передающая событие, но не вложенным детям?

Я создал аккордеонную директиву на Angular, которая может быть вложенной, чтобы в аккордеоне могли быть дети аккордеоны.

Я хочу транслировать событие, когда аккордеон открывается и закрывается, чтобы другие директивы могли его прослушивать (например, меню внутри панели аккордеона может раскрыться, когда аккордеон внутри него открыт).

Дело в том, что если во внешнем есть вложенный внутренний аккордеон, я не хочу, чтобы событие транслировалось дочерним элементам внутреннего аккордеона, потому что внутренний аккордеон не транслировал событие открытия / закрытия.

На всякий случай, если это не имеет смысла, иначе говоря, элемент внутри вложенного аккордеона должен иметь возможность прослушивать событие открытия / закрытия, транслируемое аккордеоном, в котором он находится, а не тем, которое находится дальше по дереву DOM.

Надеюсь, есть простое решение для этого.

Обновление: добавлено демо здесь: http://jsfiddle.net/jonhobbs/xr1kLqba/

Я явно не понял $broadcast и $on должным образом, поскольку демонстрационные события в настоящее время вообще не работают, но должно быть ясно, что я пытаюсь сделать!

Изменить: Очевидно, что все ссылки на jsfiddle должны сопровождаться кодом, так что вот некоторые.

var app = angular.module('app', []);

app.controller('MainCtrl', function($scope) {
  $scope.mainMenuOpen = true;
})

.directive('myPanel', function() {
  return {
    restrict: 'EA',
    scope: {
      isOpen: '=?'
    },
    link: function($scope, $element, $attrs) {  
      $element.find('> BUTTON').bind('click', function() {

        $scope.isOpen = !$scope.isOpen;
        if ($scope.isOpen) {
          $scope.$broadcast('panelOpened');
        }
      });
    }
  };
})

.directive('reactToPanelOpenClose', function() {
  return {
    restrict: 'EA',
    scope: {},
    link: function($scope, $element, $attrs) {
      $scope.$on('panelOpened', function() {

        alert("clicked");

        $element.css({ 'background-color': 'red' });
      });
    }
  };
});

3 ответа

Решение

Безусловно, самый простой подход заключается в использовании require как вы упоминали в комментариях.

Одним из способов решения этой проблемы является регистрация дочерних контроллеров (или даже их функций обратного вызова), и тогда родитель может вызывать каждого потомка вместо широковещания.

Здесь следует отметить, что вам нужно require: "?^^parent" - смысл, сделать обязательным необязательный и строго предковый селектор; за "^" Angular вернул бы себя.

.directive("accordion", function(){
  return {
    require: ["accordion", "?^^accordion"],
    controller: function(){
       var children = [];
       this.registerChild = function(childCtrl){
          children.push(childCtrl);
       }

       this.handleNotification = function(){
          // do something when notification arrives from parent
       }

       this.notify = function(){
          angular.forEach(children, function(child){
            child.handleNotification();
          })
       }
    },
    link: function(scope, element, attrs, ctrls){
      var me = ctrls[0], parent = ctrls[1];

      if (parent){
         parent.registerChild(me);
      } 
    }
  }
})

Вы пытаетесь работать с угловым контекстом внутри обратного вызова jQuery, так что это не сработает. Самый простой подход к решению вашей проблемы - следующий фрагмент кода:

angular.$externalBroadcast = function (element, event, data) {
    var scope = element.scope();
    scope.$apply(function () {
        scope.$broadcast(event, data);
    });
};

И примените эту функцию внутри функции ссылки вашей директивы панели:

$scope.isOpen = !$scope.isOpen;                
if($scope.isOpen){
    angular.$externalBroadcast($element, 'panelOpened');
}

Посмотрите на эту модифицированную скрипку.

Вы можете добавить числовое значение depth или аналог вашей трансляции, которая определяет иерархическую глубину, с которой была отправлена ​​трансляция. если контроллер слушает директивы, имеет depth из 1 выше, это может выполнить. Иначе он мог просто проигнорировать трансляцию.

Если неясно, я мог бы также добавить небольшой пример, просто дайте мне знать.

Другие вопросы по тегам