使用 $scope.$emit 和 $scope.$on

IT技术 javascript angularjs
2021-01-24 05:36:25

如何$scope使用.$emit.$on方法对象从一个控制器发送到另一个控制器

function firstCtrl($scope) {
    $scope.$emit('someEvent', [1,2,3]);
}

function secondCtrl($scope) {
    $scope.$on('someEvent', function(mass) { console.log(mass); });
}

它不像我认为的那样工作。如何做$emit$on工作?

6个回答

首先,父子作用​​域关系很重要。您有两种可能性来发出一些事件:

  • $broadcast -- 向下调度事件到所有子作用域,
  • $emit -- 通过作用域层次向上调度事件。

我对您的控制器(范围)关系一无所知,但有几种选择:

  1. 如果 scope offirstCtrl作用域的父级,则secondCtrl您的代码应该通过替换$emit$broadcastin来工作firstCtrl

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
    
  2. 如果您的范围之间没有父子关系,您可以注入$rootScope控制器并将事件广播到所有子范围(即也secondCtrl)。

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
  3. 最后,当您需要将事件从子控制器分派到范围向上时,您可以使用$scope.$emit. 如果范围firstCtrl范围的父级secondCtrl

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    
有没有办法将事件从服务触发到控制器?
2021-03-17 05:36:25
是的,理论上您可以注入$rootScope您的服务并从服务广播事件。
2021-03-29 05:36:25
@Zlatko 我很确定默认情况下服务是无作用域的,您需要一个作用域来参与事件系统。所以你需要为你的服务提供一个范围。$rootScope 是最通用的解决方案,但是如果您希望您的服务从不同的范围发送事件,您的控制器可以通过在服务上设置一个属性将其范围传递给服务,现在服务可以使用控制器的范围。一种更直接的技术可能是控制器为服务提供一个服务可以直接调用的功能。
2021-03-30 05:36:25
如果你使用 iframe,这篇文章会很有帮助charemza.name/blog/posts/angularjs/iframe/...
2021-04-09 05:36:25
服务可以注入$rootScope- 但我想知道如果我从服务(关闭$rootScope发出一个事件,该事件仍然会渗透到$rootScope; 因为,如果$broadcast向下渗透层次结构,然后$emit向上渗透——“向上”和“向下”之间会发生什么——因为广播者/发射者也是听众(?)。如果我希望事件对所有“UPWARD”和所有“DOWNWARD”范围保持静音,但只在与调度员相同的级别上“听得见”怎么办?
2021-04-09 05:36:25

我还建议将第四个选项作为@zbynour 提出的选项的更好替代方案。

使用$rootScope.$emit而不是$rootScope.$broadcast不管发送和接收控制器之间的关系。这样,事件保持在集合中,$rootScope.$$listeners$rootScope.$broadcast事件传播到所有子作用域,其中大部分可能无论如何都不会成为该事件的侦听器。当然,在接收控制器的一端,您只需使用$rootScope.$on.

对于此选项,您必须记住销毁控制器的 rootScope 侦听器:

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
根据@FilipSobczak 评论,您可以通过使用以下代码jsfiddle.net/ndqexjsg/1在 $destroy 事件上解除绑定处理程序来避免这种不需​​要的行为
2021-03-18 05:36:25
请注意 $rootScope 永远存在。如果您的控制器运行两次,其中的任何 $rootScope.$on 将运行两次,并且捕获的事件将导致回调调用两次。如果您改用 $scope.$on,回调将与您的控制器一起被 AngularJS 隐式销毁。
2021-03-22 05:36:25
@ThalisK。感谢您提供此选项。它避免了传播,但另一方面它需要$rootScope注入控制器(通常不需要)。但肯定是另一种选择,谢谢!
2021-03-29 05:36:25
从某种意义上说是的,好处是您可以避免事件传播。
2021-04-06 05:36:25
这将基本上充当中央事件总线,对吗?
2021-04-08 05:36:25

如何使用 .$emit 和 .$on 方法将我的 $scope 对象从一个控制器发送到另一个控制器?

您可以在应用程序的层次结构中发送任何您想要的对象,包括$scope

这是关于广播发射如何工作的一个快速概念

注意下面的节点;全部嵌套在节点 3中。当您遇到这种情况时,您可以使用广播发射

注意:本例中每个节点的数量是任意的;它很容易成为第一;第二个;甚至是 1,348。每个数字只是这个例子的一个标识符。这个例子的重点是展示 Angular 控制器/指令的嵌套。

                 3
           ------------
           |          |
         -----     ------
         1   |     2    |
      ---   ---   ---  ---
      | |   | |   | |  | |

看看这棵树。你如何回答以下问题?

注意:还有其他方法可以回答这些问题,但在这里我们将讨论广播发射此外,在阅读以下文本时,假设每个数字都有自己的文件(指令、控制器),例如 one.js、two.js、three.js。

如何节点1所讲至节点3

在文件one.js 中

scope.$emit('messageOne', someValue(s));

在文件three.js - 需要通信的所有子节点的最上层节点。

scope.$on('messageOne', someValue(s));

节点 2 如何与节点 3 对话?

在文件two.js 中

scope.$emit('messageTwo', someValue(s));

在文件three.js - 需要通信的所有子节点的最上层节点。

scope.$on('messageTwo', someValue(s));

节点 3 如何与节点 1 和/或节点 2 通信?

在文件three.js - 需要通信的所有子节点的最上层节点。

scope.$broadcast('messageThree', someValue(s));

在文件one.js && two.js 中,您想要捕获消息的任何文件或两者。

scope.$on('messageThree', someValue(s));

节点 2 如何与节点 1 对话?

在文件two.js 中

scope.$emit('messageTwo', someValue(s));

在文件three.js - 需要通信的所有子节点的最上层节点。

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});

在文件one.js 中

scope.$on('messageTwo', someValue(s));

然而

当您让所有这些嵌套的子节点尝试像这样进行通信时,您会很快看到许多$on's$broadcast's$emit's

这是我喜欢做的事情。

在最上面的父节点(在这种情况下为3 ...),它可能是您的父控制器...

所以,在文件three.js

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});

现在在任何子节点中,您只需要$emit消息或使用$on捕获它

注意:在不使用$emit$broadcast$on 的情况下,在一个嵌套路径中串扰通常很容易,这意味着大多数用例都用于尝试让节点1与节点2进行通信,反之亦然。

节点 2 如何与节点 1 对话?

在文件two.js 中

scope.$emit('pushChangesToAllNodes', sendNewChanges());

function sendNewChanges(){ // for some event.
  return { name: 'talkToOne', data: [1,2,3] };
}

在文件three.js - 需要通信的所有子节点的最上层节点。

我们已经处理过这个了还记得吗?

在文件one.js 中

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});

您仍然需要将$on与您想要捕获的每个特定值一起使用,但是现在您可以在任何节点中创建您喜欢的任何内容,而不必担心在我们捕获和广播时如何通过父节点间隙获取消息通用pushChangesToAllNodes

希望这可以帮助...

3、2 和 1 是嵌套控制器或指令。在创建应用程序时,请记住您的嵌套并应用上述逻辑。例如,我们可以说 3 是应用程序的 $rootScope;一切都嵌套在它下面。3、2 和 1 是任意的。
2021-03-16 05:36:25
很好的例子!但我仍然认为最好在父级中使用自己的事件调度程序来通信控制器组。将调度程序的创建保留为服务以将其用作模式也很有用。
2021-04-01 05:36:25
如何决定哪一个是 3,2 和 1?
2021-04-06 05:36:25
根据$broadcast 上的 angular 文档, The event life cycle starts at the scope on which $broadcast was called. All listeners listening for name event on this scope get notified. 因此如果您在 ctrl3$on('x', function(e, data) { $broadcast('x', data) })实现 ctrl1 与 ctrl2 对话,您(像我一样)将获得无限循环在广播之前,您将需要这些行;if (e.targetScope.$id === $scope.$id) { return; }
2021-04-07 05:36:25

要发送$scope object从一个控制器到另一个,我将谈$rootScope.$broadcast$rootScope.$emit,因为它们是最常用在这里。

案例一

$rootScope.$broadcast:-

$rootScope.$broadcast('myEvent',$scope.data);//Here `myEvent` is event name

$rootScope.$on('myEvent', function(event, data) {} //listener on `myEvent` event

$rootScope侦听器不会自动销毁。您需要使用$destroy. 最好使用,$scope.$on因为监听器$scope会自动销毁,即一旦 $scope 被销毁。

$scope.$on('myEvent', function(event, data) {}

或者,

  var customeEventListener = $rootScope.$on('myEvent', function(event, data) {

  }
  $scope.$on('$destroy', function() {
        customeEventListener();
  });

案例2:

$rootScope.$emit:

   $rootScope.$emit('myEvent',$scope.data);

   $rootScope.$on('myEvent', function(event, data) {}//$scope.$on not works

$emit 和 $broadcast 的主要区别在于 $rootScope.$emit 事件必须使用 $rootScope.$on 监听,因为发出的事件永远不会通过作用域树下降。.
在这种情况下,您也必须像 $broadcast 一样销毁侦听器。

编辑:

我宁愿不使用,$rootScope.$broadcast + $scope.$on而是使用 $rootScope.$emit+ $rootScope.$on. $rootScope.$broadcast + $scope.$on组合可能会导致严重的性能问题。那是因为事件将在所有范围内冒泡。

编辑2

此答案中解决的问题已在 angular.js 版本 1.2.7 中解决。$broadcast 现在避免在未注册的范围内冒泡,并且运行速度与 $emit 一样快。

您必须使用 $rootScope 在同一应用程序中的控制器之间发送和捕获事件。将 $rootScope 依赖项注入您的控制器。这是一个工作示例。

app.controller('firstCtrl', function($scope, $rootScope) {        
        function firstCtrl($scope) {
        {
            $rootScope.$emit('someEvent', [1,2,3]);
        }
}

app.controller('secondCtrl', function($scope, $rootScope) {
        function secondCtrl($scope)
        {
            $rootScope.$on('someEvent', function(event, data) { console.log(data); });
        }
}

链接到 $scope 对象的事件只在所有者控制器中工作。控制器之间的通信是通过 $rootScope 或服务完成的。