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

Pau*_*nko 880 javascript angularjs

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

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

function secondCtrl($scope) {
    $scope.$on('someEvent', function(mass) { console.log(mass); });
}
Run Code Online (Sandbox Code Playgroud)

它不像我认为的那样工作.怎么做$emit$on工作?

zby*_*our 1496

首先,父子范围关系确实很重要.你有两种可能发出一些事件:

  • $broadcast - 将事件向下发送到所有子范围,
  • $emit - 通过范围层次结构向上调度事件.

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

  1. 如果范围firstCtrl是母公司secondCtrl范围,你的代码应该通过更换工作$emit$broadcastfirstCtrl:

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

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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]);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 是的,理论上你可以将`$ rootScope`注入你的服务并从服务中广播事件. (29认同)
  • @Zlatko我非常确定默认服务是无范围的,你需要一个范围来参与事件系统.所以你需要为你的服务提供一个范围.$ rootScope是最通用的解决方案,但如果您希望您的服务从不同的范围发送事件,您的控制器可以通过在服务上设置属性将其范围传递给服务,现在服务可以使用控制器的范围.更直接的技术可能是控制器向服务提供功能,服务可以直接调用. (13认同)
  • 有没有办法将事件从服务发送到控制器? (8认同)
  • 我根据@ Zlatko的评论做了[这个plunkr](http://plnkr.co/edit/bYXBCm?p=preview),它就像一个魅力.它似乎远没有传递给`$ rootScope`那么笨拙. (7认同)
  • 如果您使用的是iframe,那么本文将非常有用http://charemza.name/blog/posts/angularjs/iframe/same-domain-iframe-communication/ (3认同)
  • 服务可以注入`$rootScope`——但我想知道,如果我从服务(从`$rootScope` 发出)发出一个事件,该事件仍然会渗透到`$rootScope`;因为,如果`$broadcast` 向下渗透层次结构,而`$emit` 向上渗透——“UP”和“DOWN”之间会发生什么——因为广播者/发射者也是听众(?)。如果我希望事件对所有“UPWARD”和所有“DOWNWARD”范围保持静音,但只在与调度员相同的级别上“听得见”怎么办? (2认同)

Tha*_* K. 145

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

使用$rootScope.$emit而不是$rootScope.$broadcast不管传输和接收控制器之间的关系.这样,事件保持在一组内,$rootScope.$$listeners$rootScope.$broadcast事件传播到所有子范围,其中大多数可能不是该事件的监听者.当然,在接收控制器的最后你只需要使用$rootScope.$on.

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

var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
$scope.$on('$destroy', function () {
  unbindEventHandler();
});
Run Code Online (Sandbox Code Playgroud)

  • 请注意$ rootScope永远存在.如果您的控制器运行两次,其内部的任何$ rootScope.$ on将运行两次,捕获的事件将导致两次调用回调.如果您使用$ scope.$ on,则回调将与AngularJS隐式地与您的控制器一起销毁. (77认同)
  • 从某种意义上说,好处是可以避免事件传播. (5认同)
  • 那么这基本上可以作为中央事件总线正确吗? (3认同)
  • @ThalisK.谢谢你的选择.它避免了传播,但另一方面它需要将`$ rootScope`注入控制器(一般不需要).但肯定是另一种选择,thx! (3认同)

SoE*_*zPz 109

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

您可以在应用层次结构中发送所需的任何对象,包括$ scope.

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

注意下面的节点; 全部嵌套在节点3 中.当您拥有此场景时,您将使用广播发出.

注意:此示例中每个节点的数量是任意的; 它很容易成为头号人物; 二号; 甚至是1,348号.每个数字只是此示例的标识符.这个例子的重点是显示Angular控制器/指令的嵌套.

                 3
           ------------
           |          |
         -----     ------
         1   |     2    |
      ---   ---   ---  ---
      | |   | |   | |  | |
Run Code Online (Sandbox Code Playgroud)

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

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

如何节点1讲至节点3

在文件one.js中

scope.$emit('messageOne', someValue(s));
Run Code Online (Sandbox Code Playgroud)

在文件three.js中 - 所有子节点的最高节点需要进行通信.

scope.$on('messageOne', someValue(s));
Run Code Online (Sandbox Code Playgroud)

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

在文件two.js中

scope.$emit('messageTwo', someValue(s));
Run Code Online (Sandbox Code Playgroud)

在文件three.js中 - 所有子节点的最高节点需要进行通信.

scope.$on('messageTwo', someValue(s));
Run Code Online (Sandbox Code Playgroud)

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

在文件three.js中 - 所有子节点的最高节点需要进行通信.

scope.$broadcast('messageThree', someValue(s));
Run Code Online (Sandbox Code Playgroud)

在文件one.js && two.js中您要捕获消息的文件或两者.

scope.$on('messageThree', someValue(s));
Run Code Online (Sandbox Code Playgroud)

节点2如何与节点1通信?

在文件two.js中

scope.$emit('messageTwo', someValue(s));
Run Code Online (Sandbox Code Playgroud)

在文件three.js中 - 所有子节点的最高节点需要进行通信.

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});
Run Code Online (Sandbox Code Playgroud)

在文件one.js中

scope.$on('messageTwo', someValue(s));
Run Code Online (Sandbox Code Playgroud)

然而

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

这就是我喜欢做的事情.

在最高的PARENT NODE(在这种情况下为3 ......),这可能是你的父控制器......

所以,在文件three.js中

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});
Run Code Online (Sandbox Code Playgroud)

现在,在任何子节点中,您只需要$ $发出消息或使用$ 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] };
}
Run Code Online (Sandbox Code Playgroud)

在文件three.js中 - 所有子节点的最高节点需要进行通信.

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

在文件one.js中

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助...


Ved*_*Ved 39

要从$scope object一个控制器发送到另一个控制器,我将讨论$rootScope.$broadcast$rootScope.$emit在这里使用最多.

案例1:

$ rootScope $广播: -

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

$rootScope.$on('myEvent', function(event, data) {} //listener on `myEvent` event
Run Code Online (Sandbox Code Playgroud)

$rootScope监听器不会自动销毁.你需要使用它来销毁它$destroy.最好使用$scope.$onas listener $scope自动销毁,即只要$ scope被销毁.

$scope.$on('myEvent', function(event, data) {}
Run Code Online (Sandbox Code Playgroud)

要么,

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

  }
  $scope.$on('$destroy', function() {
        customeEventListener();
  });
Run Code Online (Sandbox Code Playgroud)

案例2:

.$ rootScope $发出:

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

   $rootScope.$on('myEvent', function(event, data) {}//$scope.$on not works
Run Code Online (Sandbox Code Playgroud)

$ 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一样快.


kya*_*sar 10

您必须使用$ 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); });
        }
}
Run Code Online (Sandbox Code Playgroud)

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


小智 7

您可以从控制器调用返回承诺的服务,然后在控制器中使用它.并进一步使用$emit$broadcast通知其他控制器.在我的情况下,我不得不通过我的服务进行http调用,所以我做了类似这样的事情:

function ParentController($scope, testService) {
    testService.getList()
        .then(function(data) {
            $scope.list = testService.list;
        })
        .finally(function() {
            $scope.$emit('listFetched');
        })


    function ChildController($scope, testService) {
        $scope.$on('listFetched', function(event, data) {
            // use the data accordingly
        })
    }
Run Code Online (Sandbox Code Playgroud)

我的服务看起来像这样

    app.service('testService', ['$http', function($http) {

        this.list = [];

        this.getList = function() {
            return $http.get(someUrl)
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        list = response.data.results;

                        return response.data;
                    } else {
                        // invalid response
                        return $q.reject(response.data);
                    }

                }, function(response) {
                    // something went wrong
                    return $q.reject(response.data);
                });

        }

    }])
Run Code Online (Sandbox Code Playgroud)