关于javascript:使用$ scope.$ emit和$ scope.$ on

Working with $scope.$emit and $scope.$on

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

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

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

我认为这不可行。$emit$on如何工作?


首先,父子范围关系很重要。您有两种可能发出某些事件:

  • $broadcast--将事件向下发送到所有子作用域,
  • $emit--通过作用域层次结构向上发送事件。

我对您的控制器(作用域)关系一无所知,但有以下几种选择:

  • 如果firstCtrl的作用域是secondCtrl作用域的父级,您的代码应该用firstCtrl中的$broadcast替换$emit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }

    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
  • 如果您的作用域之间没有父子关系,则可以将$rootScope注入控制器并广播事件所有子范围(也就是secondCtrl)。

    1
    2
    3
    4
    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
  • 最后,当您需要从子控制器调度事件时要向上搜索,可以使用$scope.$emit。如果firstCtrl的范围是secondCtrl范围的父项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }

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

  • 我还建议使用第四个选项,作为@zbynour提议的选项的更好替代方案。

    使用$rootScope.$emit而不是$rootScope.$broadcast,不管传输和接收控制器之间的关系如何。这样,事件仍在$rootScope.$$listeners的集合中,而在$rootScope.$broadcast的集合中,事件传播到所有子范围,其中大部分可能不会是该事件的侦听器。当然,在接收控制器端,您只需使用$rootScope.$on

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

    1
    2
    3
    4
    var unbindEventHandler = $rootScope.$on('myEvent', myHandler);
    $scope.$on('$destroy', function () {
      unbindEventHandler();
    });


    How can I send my $scope object from one controller to another using .$emit and .$on methods?

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

    这里有一个关于广播和发射如何工作的快速想法。

    注意下面的节点;所有都嵌套在节点3中。当您有这个场景时,可以使用广播和发射。

    注意:本例中每个节点的编号是任意的;可以很容易地是数字1、数字2,甚至是数字1348。对于这个例子,每个数字只是一个标识符。本例的重点是显示角度控制器/指令的嵌套。

    1
    2
    3
    4
    5
    6
    7
                     3
               ------------
               |          |
             -----     ------
             1   |     2    |
          ---   ---   ---  ---
          | |   | |   | |  | |

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

    注意:还有其他方法来回答这些问题,但这里我们将讨论广播和发射。另外,在阅读下面的文本时,假设每个数字都有自己的文件(指令、控制器)e.x.one.js、2.js和3.js。

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

    在文件ON.JS中

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

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

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

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

    在文件中

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

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

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

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

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

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

    在文件one.js&;&;two.js中,无论您想捕获消息的哪个文件,或者两者都要捕获。

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

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

    在文件中

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

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

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

    在文件ON.JS中

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

    然而

    When you have all these nested child nodes trying to communicate like this, you will quickly see many $on's, $broadcast's, and $emit's.

    这是我喜欢做的。

    在最上面的父节点中(在本例中为3…),可能是您的父控制器…

    所以,在文件3.js中

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

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

    注意:在一个嵌套的路径中,不使用$emit、$broadcast或$on就可以很容易地进行交叉对话,这意味着大多数用例都是在您试图让节点1与节点2通信时使用的,反之亦然。

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

    在文件中

    1
    2
    3
    4
    5
    scope.$emit('pushChangesToAllNodes', sendNewChanges());

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

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

    我们已经处理过了,记得吗?

    在文件ON.JS中

    1
    2
    3
    4
    5
    scope.$on('talkToOne', function( event, arrayOfNumbers ){
      arrayOfNumbers.forEach(function(number){
        console.log(number);
      });
    });

    您仍然需要将$on用于要捕获的每个特定值,但现在您可以在任何节点中创建您喜欢的任何内容,而无需担心在捕获和广播通用pushchangestoallnodes时如何跨越父节点间隙获取消息。

    希望这有帮助…


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

    案例1:

    $rootscope.$broadcast:-

    1
    2
    3
    $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被销毁。

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

    或者,

    1
    2
    3
    4
    5
    6
      var customeEventListener = $rootScope.$on('myEvent', function(event, data) {

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

    案例2:

    $rootscope.$emit:

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

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

    $emit和$broadcast的主要区别在于$rootscope.$emit事件必须使用$rootscope.$on进行侦听,因为所发出的事件从未通过作用域树发生。在这种情况下,您还必须像$broadcast那样销毁侦听器。

    编辑:

    I prefer not to use $rootScope.$broadcast + $scope.$on but use
    $rootScope.$emit+ $rootScope.$on. The $rootScope.$broadcast +
    $scope.$on
    combo can cause serious performance problems. That is
    because the event will bubble down through all scopes.

    编辑2:

    The issue addressed in this answer have been resolved in angular.js
    version 1.2.7. $broadcast now avoids bubbling over unregistered scopes
    and runs just as fast as $emit.


    必须使用$rootscope在同一应用程序中的控制器之间发送和捕获事件。向控制器注入$rootscope依赖项。这是一个有效的例子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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或服务完成。


    您可以从控制器调用返回承诺的服务,然后在控制器中使用该服务。并进一步使用$emit$broadcast通知其他管制员。在我的例子中,我必须通过我的服务进行HTTP调用,所以我做了如下的事情:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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
            })
        }

    我的服务看起来像这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        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);
                    });

            }

        }])


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <!DOCTYPE html>
    <html>

    <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js">

    var app = angular.module('MyApp',[]);
    app.controller('parentCtrl',function($scope){
      $scope.$on('MyEvent',function(event,data){    
        $scope.myData = data;
      });
     });

    app.controller('childCtrl',function($scope){
      $scope.fireEvent = function(){
      $scope.$emit('MyEvent','Any Data');
      }  
     });

    </head>
    <body ng-app="MyApp">


    {{myData}}

     
       <button ng-click="fireEvent()">Fire Event</button>
     


    </body>
    </html>

    这是我的职责:

    1
    2
    3
    4
    5
    6
    7
    8
    $rootScope.$emit('setTitle', newVal.full_name);

    $rootScope.$on('setTitle', function(event, title) {
        if (scope.item)
            scope.item.name = title;
        else
            scope.item = {name: title};
    });


    最后,我添加了一个外部EventEmitter库作为一个服务来项目,并在我需要的地方注入它。所以我可以在任何地方"发出"和"打开"任何东西,而不需要考虑范围继承。这样做的麻烦更小,性能也更好。对我来说也更具可读性。

    通配符支持:EventEmitter2

    性能良好:EventEmitter3

    其他备选方案:滴灌


    作用域可用于向作用域子级或父级传播、分派事件。

    $emit-将事件传播给父级。$broadcast-将活动传播给儿童。$on-侦听事件的方法,由$emit和$broadcast传播。

    index.html示例:

    1
    2
    3
    4
    5
    6
          Root(Parent) scope count: {{count}}
     
          <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
          <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>

          Childrent scope count: {{count}}

    示例App.js:

    1
    2
    3
    4
    5
    6
    7
    angular.module('appExample', [])
    .controller('EventCtrl', ['$scope', function($scope) {
      $scope.count = 0;
      $scope.$on('MyEvent', function() {
        $scope.count++;
      });
    }]);

    在这里,您可以测试代码:http://jsfiddle.net/zp6v0rut/41/


    下面的代码显示两个子控制器,从中向上调度事件到父控制器(rootscope)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <body ng-app="App">

       

            <p>
    City : {{city}}
    </p>
            <p>
     Address : {{address}}
    </p>

           
                <input type="text" ng-model="city" />
                <button ng-click="getCity(city)">City !!!</button>
           

           

                <input type="text" ng-model="address" />
                <button ng-click="getAddrress(address)">Address !!!</button>

           

       

    </body>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    var App = angular.module('App', []);

    // parent controller
    App.controller('parentCtrl', parentCtrl);

    parentCtrl.$inject = ["$scope"];

    function parentCtrl($scope) {

        $scope.$on('cityBoom', function(events, data) {
            $scope.city = data;
        });

        $scope.$on('addrBoom', function(events, data) {
            $scope.address = data;
        });
    }

    // sub controller one

    App.controller('subCtrlOne', subCtrlOne);

    subCtrlOne.$inject = ['$scope'];

    function subCtrlOne($scope) {

        $scope.getCity = function(city) {

            $scope.$emit('cityBoom', city);    
        }
    }

    // sub controller two

    App.controller('subCtrlTwo', subCtrlTwo);

    subCtrlTwo.$inject = ["$scope"];

    function subCtrlTwo($scope) {

        $scope.getAddrress = function(addr) {

            $scope.$emit('addrBoom', addr);  
        }
    }

    http://jsfiddle.net/shushanthp/zp6v0rut/


    最简单的方法:

    HTML

    1
            <button ng-click="sendData();"> Send Data </button>

    JavaScript

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
            var app = angular.module('myApp', []);
            app.controller('myCtrl', function($scope, $rootScope) {
                function sendData($scope) {
                    var arrayData = ['sam','rumona','cubby'];
                    $rootScope.$emit('someEvent', arrayData);
                }

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

    根据AngularJS事件文档,接收端应该包含具有如下结构的参数

    @帕拉姆

    --对象事件是包含事件信息的事件对象

    --对象被调用方传递的参数(请注意,只有这样才能更好地发送到字典对象中)

    $scope.$on('fooEvent', function (event, args) { console.log(args) });从你的代码

    另外,如果您试图在不同的控制器上获得共享的信息,还有另一种实现这一点的方法,那就是角度服务。由于服务是单件的,所以可以跨控制器存储和获取信息。只需在该服务中创建getter和setter函数,就可以公开这些函数,在服务中生成全局变量并使用它们存储信息