【问题标题】:$broadcast event not received by controller控制器未收到 $broadcast 事件
【发布时间】:2014-07-28 15:38:11
【问题描述】:

所以我有一个 angularjs 应用程序,它有几个局部视图,每个局部视图一个控制器。我还有一项服务,它会从 $rootScope 中播放 $broadcast 事件:

$rootScope.$broadcast('MyEvent', 'message');

目前只有一个控制器在监听事件,我注意到如果我不在与那个控制器关联的视图上(通过路由),那么该控制器永远不会收到事件,因此永远不会加载视图与之相关联。无论哪个视图处于活动状态,如何确保控制器接收到事件广播?我尝试在我的控制器中使用 $scope.$on('MyEvent')... 和 $rootScope.$on('MyEvent')... ;似乎都不起作用。

【问题讨论】:

  • 你能提供一个小提琴或plunker吗?
  • 显然,当您不在带有控制器的视图上时,它不会收到事件,因为控制器和侦听器都不存在,但如果您已经有服务,您可以将其注入控制器并制作使用双向数据绑定,即当控制器初始化和服务注入检查值时服务并做某事
  • AFAIK,根据 ngRoute 的设计,控制器将在用户进入关联视图时创建。当用户离开时,控制器将被销毁。

标签: angularjs events global broadcast


【解决方案1】:

如果您包含更多代码会有所帮助,因为执行 $on() 有正确的方法和错误的方法,而您的速记方式是错误的。我曾经看到有人认为这会返回一个承诺并试图在其上使用 .then() ,但这是行不通的。它需要一个回调:

$rootScope.$on('MyEvent', function(myParam) {
    alert(myParam);
});

另外,如果您在做 pub/sub 类型的工作,请注意 $emit 和 $broadcast 之间的区别。出于效率原因,这很重要,如果您不需要实际使用 $broadcast ,您可能需要重新考虑它。 $broadcast 将在该范围以及所有子范围上发送消息。这意味着 Angular 需要递归地遍历每个范围以寻找事件的侦听器。如果您使用大量指令(其中许多有 scopy)和 ngRepeats,您可能会拥有数千个这样的指令!

当您想故意将某些内容放在特定范围及其子范围内时,通常会使用 $broadcast,例如当您在数据表行中的兄弟姐妹之间发布事件或类似的事情时。 (悬停效果,行数据更新,诸如此类...)

您可以改为使用 $emit。其工作方式相同(您仍然使用 $on 来监听它),但这次它向上 进入作用域链。这意味着如果您在 $rootScope 上启动它,它只会点击一个快速的侦听器列表并停在那里 - 如果您在同一级别注册您的 $on() 事件,它会非常快速和高效。在这种情况下,您将使用 $rootScope.$on() 而不是 $scope.$on()... 但效率提升是值得的!

【讨论】:

  • 这非常有用,但我认为这不是问题的答案。控制器中的 pub/sub 仅适用于活动视图。
  • 是的抱歉,广播服务中的代码 sn-p 实际上是:` $rootScope.$broadcast('MyEvent', msgData);` 如果视图处于活动状态,它可以正常工作,否则不会没有什么。我会考虑使用 $emit 作为更有效的替代方案,但我不相信它会解决我的问题。
  • 呸,你说得对,我错过了那个细节。 :( 没有办法解决这个问题 - 你的评论是正确的,ngRoute 是按设计这样做的。他必须创建一个服务(或一个一直在工作的控制器)并在那里监听这个事件,然后让它可用需要时使用控制器。
  • 直接从服务调用控制器怎么样?不是最好的解决方案,因为它增加了两者之间的显式耦合,但这可能吗?
  • 是的,我现在在我正在开发的几个应用程序中执行此操作。通常的方法是服务 - 它非常适合这个。该服务建立数据连接(在我的例子中是 WebSockets,但它可以是任何东西)管理表示到目前为止消息已发送状态的全局数据对象。它将这些作为变量公开在服务中,因此注入它的任何人也可以看到它们。让我们假设它是一个 IM 好友列表。服务可以调用路由器来触发状态视图,控制器可以只渲染数据并提供点击处理程序。简单、实用,而且效果很好。
【解决方案2】:

这是预期的行为,因为您的局部视图具有与之关联的单独控制器。仅当呈现视图时,控制器才会在范围内。仅当您在局部视图中解析 ng-controller 指令时,控制器才会进入范围。因此,仅在加载与之关联的视图后,控制器才会在范围内。

在您的情况下,当您使用 $rootScope.$broadcast('MyEvent', 'message'); 广播事件 myEvent 时,您想要接收此事件的控制器不在范围内。

要清楚地看到这一点,请在您的broadcast 步骤上设置一个断点并暂停执行以检查控制台中angular.element('[ng-controller=urControllerName]').scope() 的值,您可以看到它是未定义的,这意味着控制器尚未在范围内。

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,因为我使用的是在通过 ng-include 包含的视图中声明的控制器,就像这样简单:

    <div ng-include="'pageToInclude.view.html'" ></div>
    

    pageToInclude 包含:

    <div class="header" ng-controller="pageCtrl as pageCtrl">.......</div>
    

    我的问题是该事件在此页面呈现之前被广播,因此它没有捕获此事件,所以我所做的只是在 ng-include 的 div 中声明控制器,如下所示:

    <div ng-include="'pageToInclude.view.html'" ng-controller="pageCtrl as pageCtrl"></div>
    

    当然也可以从页面内部删除 ng-controller。

    希望我的回答能帮助到那里的人,干杯。

    【讨论】:

      【解决方案4】:

      使用 $scope 的 $apply 方法。

      /* Service.js */
      $rootScope.$broadcast('MyEvent', 'message');
      
      
      /* Controller.js */
      $rootScope.$on("MyEvent", function(event, args){
      
        // works!
        alert("Event!");
      
        // not works!
        $scope.message = args[0];
      
        // works!
        $scope.$apply(function(){
          $scope.message = args[0];
        });
      });
      

      【讨论】:

      • $rootScope 是否应该执行摘要循环?我想用 jsfiddle 来确定这一点.. 过一会儿我会尝试一下
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-15
      • 2014-11-05
      • 2020-06-03
      • 1970-01-01
      相关资源
      最近更新 更多