【问题标题】:Custom Angular directive broadcasting an event but not to nested children?自定义Angular指令广播事件但不向嵌套子级广播?
【发布时间】:2015-07-13 05:35:04
【问题描述】:

我在 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' });
      });
    }
  };
});

【问题讨论】:

  • 一些代码在这里有所帮助,例如您的事件的名称,您正在广播/收听的范围等......
  • 对不起,这里可能有太多代码要发布,并且将其削减以制作 jsfiddle 将是一个挑战。我可以调用任何我想要的事件,因为它们目前不存在,并假设我将从我创建的手风琴指令中广播它们。老实说,它不需要与我的手风琴指令有关,它更多的是关于热播事件的一般性问题,但当您收到相同类型的指令时停止。
  • 好吧,说实话,因为$broadcast 不能被收件人取消,所以您刚才描述的场景不容易建模。最简单的方法可能是以编程方式生成用于事件名称的字符串键,并以某种方式将该值传递给需要监听的元素,以便每个手风琴都是独一无二的。
  • 谢谢,这是有道理的。我有一种感觉,$broadcast 将无法取消。我目前正在探索在子元素上使用 require:^accordian 并让他们使用手风琴注册回调或其他东西而不是使用 $broadcast/$on
  • 阅读本文时,问题听起来相对复杂,但解决方案可能相对简单。我真的不知道在一个简化的演示中模拟这个需要很长时间,为了更好地理解你正在使用的东西来创建它不需要很长时间......垃圾输入,垃圾输出

标签: javascript angularjs angularjs-directive angular-broadcast


【解决方案1】:

到目前为止,最简单的方法是使用您在 cmets 中提到的 require。

解决这个问题的一种方法是注册子控制器(甚至是它们的回调函数),然后父控制器可以调用每个子控制器而不是广播。

这里要注意的是,您需要 require: "?^^parent" - 意思是,将 require 设为可选且严格的祖先选择器;对于"^" Angular 会返回 self。

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

【讨论】:

  • 谢谢,我想我可以修改这段代码并让它工作。实际上,当手风琴打开时需要做出反应的不是子手风琴,而是另一个完全需要它并注册回调的指令,所以我不必担心“自我”问题,我只需要将手风琴上的 registerChild 和 notify 函数以及正在向手风琴注册自身的另一个指令上的 handleNotification。
【解决方案2】:

你正在尝试在 jQuery 回调中使用 Angular 上下文,所以这是行不通的。解决您的问题的最简单方法是使用以下代码 sn-p:

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

看看这个修改后的Fiddle。

【讨论】:

  • 谢谢,但是为什么在 $scope.$apply() 中封装对 $broadcast 的调用不起作用,为什么需要创建外部函数?
  • 实际上,如果你这样做,它确实有效.... $scope.$apply(function () { $element.scope().$broadcast('panelOpened'); }); ...但如果您使用 $scope.$broadcast() 则不会,它必须是 $element.scope().$broadcast()
【解决方案3】:

您可以添加一个数字值depth 或类似于您的广播,它定义了发送广播的层次深度。如果指令控制器正在侦听,则 depth 的 1 更高,它可以执行。否则它可能会忽略广播。

如果不清楚,我还可以添加一个小例子,请告诉我。

【讨论】:

  • 如果有帮助,我只是添加了一个 jsfiddle。
猜你喜欢
  • 2016-01-25
  • 2016-03-01
  • 1970-01-01
  • 2019-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 2021-01-17
相关资源
最近更新 更多