使用$ scope。$ emit和$ scope。$ on


887

如何$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
仅面向未来的读者:$rootScope在可以避免的情况下,请勿用于广播/发射。
Mistalis '17

Answers:


1499

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

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

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

  1. 如果scope of firstCtrl是作用域的父级,则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。如果scope of firstCtrl是范围的父级secondCtrl

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    

8
有没有一种方法可以将事件从服务触发到控制器?
Zlatko

29
是的,从理论上讲,您可以注入$rootScope服务并通过该服务广播事件。
zbynour

13
@Zlatko我敢肯定,默认情况下,服务是无作用域的,您需要一个作用域才能参与事件系统。因此,您需要以某种方式为您的服务提供范围。$ rootScope是最通用的解决方案,但是如果您希望您的服务从其他范围发送事件,则控制器可以通过在服务上设置属性来将其范围传递给服务,现在该服务可以使用控制器的范围。更为直接的技术可能是控制器为服务提供功能,服务可以直接调用该功能。
奥兰·丹尼森

3
如果您使用的是iframe,那么本文将对charemza.name/blog/posts/angularjs/iframe/…
leticia 2014年

1
服务可以注入$rootScope-但我想知道,如果我从服务发出事件(关闭$rootScope),则该事件仍将渗透到$rootScope; 因为,如果$broadcast向下渗透层次结构,然后$emit向上渗透-“上”和“下”之间会发生什么-因为广播者/发射者也是监听者(?)。如果我希望事件在所有“向上”和“向下”范围内都保持沉默,而只能在与调度程序相同的级别上被“听到”,该怎么办?
科迪2014年

145

我还建议使用第4个选项,以替代@zbynour提出的选项。

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

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

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

3
那么这基本上可以用作中央事件总线吗?
jusopi 2014年

5
从某种意义上说是的,好处是您避免了事件传播。
塔利斯K.14年

3
@ThalisK。感谢您的选择。它避免了传播,但另一方面,它需要$rootScope注入控制器中(通常不需要)。但肯定是另一种选择,谢谢!
zbynour 2014年

77
注意$ rootScope永远存在。如果您的控制器运行两次,则其中的任何$ rootScope。$ on都将运行两次,并且捕获的事件将导致两次调用回调。如果改用$ scope。$ on,则AngularJS会隐式销毁回调以及控制器。
Filip Sobczak 2014年

1
根据@FilipSobczak的评论,您可以通过以下代码jsfiddle.net/ndqexjsg/1
Krzysztof Grzybek解开

111

如何使用。$ 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$ broadcast$ emit

这是我喜欢做的事。

在最上层的PARENT NODE(在本例中为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是哪一个?
HIRA THAKUR 2015年

3、2和1是嵌套控制器或指令。创建应用程序时,请记住嵌套并应用上面的逻辑。例如,我们可以说3是应用程序的$ rootScope;一切都嵌套在它下面。3、2和1是任意的。
SoEzPz 2015年

很好的例子!但是我仍然认为最好在父级中使用自己的事件分派器与控制器组进行通信。对于将调度程序的创建作为服务用作其模式也很有用。
DenisKolodin

1
根据$ 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; }
Renato Gama

39

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

情况1

$ 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。$ on监听$ rootScope。$ emit事件,因为发出的事件永远不会通过作用域树发生。
在这种情况下,您还必须像$ broadcast一样销毁监听器。

编辑:

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

编辑2

此答案解决的问题已在angular.js 1.2.7版中解决。$ broadcast现在可以避免在未注册的作用域上冒泡,并且运行速度与$ emit一样快。


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

链接到$ 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
        })
    }

我的服务看起来像这样

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

        }

    }])


4
<!DOCTYPE html>
<html>

<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
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');
  }  
 });
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="parentCtrl" ng-model="myName">

{{myData}}

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

</div>
</body>
</html>

2

范围可用于传播事件,将事件分发给范围子级或父级。

$ emit-将事件传播到父级。$ broadcast-将事件传播给孩子。 $ on-监听事件的方法,由$ emit和$ broadcast传播。

示例index.html

<div ng-app="appExample" ng-controller="EventCtrl">
      Root(Parent) scope count: {{count}}
  <div>
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button><br>

      Childrent scope count: {{count}} 
  </div>
</div>

示例app.js

angular.module('appExample', [])
.controller('EventCtrl', ['$scope', function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent', function() {
    $scope.count++;
  });
}]);

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


2

下面的代码显示了两个子控制器,事件从那里被向上调度到父控制器(rootScope)

<body ng-app="App">

    <div ng-controller="parentCtrl">

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

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

        <div ng-controller="subCtrlTwo">

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

        </div>

    </div>

</body>

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/


0

根据angularjs事件文档,接收端应包含具有类似结构的参数

@params

-{Object} event是包含有关事件信息的事件对象

-被调用方传递的{Object} args(请注意,这只能是一个更好的方式,以便始终发送字典对象)

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

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


0

最简单的方法:

的HTML

  <div ng-app="myApp" ng-controller="myCtrl"> 

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

    </div>

的JavaScript

    <script>
        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); 
            }); 
        });
    </script>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.