ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

仅供未来读者参考:如果可以避免,请勿将 $rootScope 用于广播/发射。

z
zbynour

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

$broadcast -- 将事件向下调度到所有子范围,

$emit——通过作用域层次向上调度事件。

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

如果 firstCtrl 的范围是 secondCtrl 范围的父级,则您的代码应该通过在 firstCtrl 中将 $emit 替换为 $broadcast 来工作: function firstCtrl($scope) { $scope.$broadcast('someEvent', [1,2,3]) ; } function secondCtrl($scope) { $scope.$on('someEvent', function(event, mass) { console.log(mass); });如果您的范围之间没有父子关系,您可以将 $rootScope 注入控制器并将事件广播到所有子范围(即 secondCtrl)。 function firstCtrl($rootScope) { $rootScope.$broadcast('someEvent', [1,2,3]);最后,当您需要将事件从子控制器向上分派到作用域时,您可以使用 $scope.$emit。如果 firstCtrl 的范围是 secondCtrl 范围的父级: function firstCtrl($scope) { $scope.$on('someEvent', function(event, data) { console.log(data); }); } 函数 secondCtrl($scope) { $scope.$emit('someEvent', [1,2,3]); }


有没有办法将事件从服务触发到控制器?
是的,理论上您可以将 $rootScope 注入您的服务并从服务中广播事件。
@Zlatko我很确定默认情况下服务是无范围的,您需要一个范围来参与事件系统。因此,您需要以某种方式为您的服务提供范围。 $rootScope 是最通用的解决方案,但是如果您希望您的服务从不同的范围发送事件,您的控制器可以通过在服务上设置属性将其范围传递给服务,现在服务可以使用控制器的范围。一种更直接的技术可能是控制器为服务提供可以直接调用的功能。
如果您使用的是 iframe,这篇文章会很有帮助charemza.name/blog/posts/angularjs/iframe/…
服务可以注入 $rootScope——但我想知道,如果我从服务(在 $rootScope 之外)发出事件,该事件仍将渗透到 $rootScope;因为,如果 $broadcast 向下渗透层次结构,并且 $emit 向上渗透——“向上”和“向下”之间会发生什么——因为广播器/发射器也是侦听器(?)。如果我希望事件对所有“UPWARD”和所有“DOWNWARD”范围保持沉默,但只能在与调度程序相同的级别上“听得见”怎么办?
c
clopez

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

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

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

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

那么这基本上可以用作中央事件总线,对吗?
从某种意义上说是的,好处是您可以避免事件传播。
@ThalisK。感谢您提供此选项。它避免了传播,但另一方面它需要将 $rootScope 注入控制器(通常不需要)。但肯定是另一种选择,谢谢!
注意 $rootScope 永远存在。如果您的控制器运行两次,则其中的任何 $rootScope.$on 都将运行两次,并且捕获的事件将导致调用两次回调。如果您改用 $scope.$on,回调将与您的控制器一起被 AngularJS 隐式销毁。
根据@FilipSobczak 评论,您可以通过使用以下代码在 $destroy 事件上取消绑定处理程序 jsfiddle.net/ndqexjsg/1 来避免这种不需要的行为
S
SoEzPz

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

这是我喜欢做的事情。

在最上面的父节点(在这种情况下为 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、2 和 1 是嵌套控制器或指令。在您创建应用程序时,请记住您的嵌套并应用上述逻辑。例如,我们可以说 3 是应用程序的 $rootScope;一切都嵌套在它下面。 3、2 和 1 是任意的。
很好的例子!但我仍然认为最好在父级中使用自己的事件调度程序来通信控制器组。对于将调度程序的创建保持为服务以将其用作模式也很有用。
根据 angular docs on $broadcast 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; }
V
Ved

要将 $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();
  });

案例二:

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


k
kyasar

您必须使用 $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 完成的。


s
sg7

您可以从控制器调用返回承诺的服务,然后在控制器中使用它。并进一步使用 $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);
                });

        }

    }])

P
Peter Mortensen

这是我的功能:

$rootScope.$emit('setTitle', newVal.full_name);

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

我认为这是不好的做法,因为您的 rootScope 会很混乱。请参阅stackoverflow.com/questions/24830679/…
P
Prashant_M
<!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>

V
Vasyl Gutnyk

Scope(s) 可用于将事件传播、分派到作用域子级或父级。

$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/


s
sg7

下面的代码显示了事件被向上调度到父控制器(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/


W
Wajih Siddiqui

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

@参数

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

-- 被调用者传递的 {Object} 参数(请注意,这只能是一个更好地发送字典对象的参数)

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

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


S
Sangwin Gawande

最简单的方法:

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>