$ on和$广播角度


282

我有一个footerController和codeScannerController具有不同的视图。

angular.module('myApp').controller('footerController', ["$scope", function($scope) {}]);

angular.module('myApp').controller('codeScannerController', ["$scope", function($scope) {
console.log("start");
$scope.startScanner = function(){...

当我<li>在footer.html中单击时,应该在codeScannerController中得到此事件。

<li class="button" ng-click="startScanner()">3</li>

我认为可以使用$on和来实现$broadcast,但是我不知道如何在任何地方都找不到示例。

Answers:


631

如果要$broadcast使用$rootScope

$scope.startScanner = function() {

    $rootScope.$broadcast('scanner-started');
}

然后使用$scope您控制器的接收:

$scope.$on('scanner-started', function(event, args) {

    // do what you want to do
});

如果愿意,可以在以下情况下传递参数$broadcast

$rootScope.$broadcast('scanner-started', { any: {} });

然后接收它们:

$scope.$on('scanner-started', function(event, args) {

    var anyThing = args.any;
    // do what you want to do
});

范围docs中有关此内容的文档


2
您可以根据自己的喜好为事件命名。
达文·崔顿

5
确保您是$ scope。$ apply(); 您的更改!
Ismail 2014年

4
@Ismail为什么...在哪里?
Jaans 2014年

7
是否有建议的做法来将这些字符串存储在某个地方,而不是对广播消息进行硬编码?
rperryng 2014年

8
@Ismail $scope.$apply()仅在更改角度框架之外的模型时才需要(例如,在setTimeout,对话框回调或ajax回调中),换句话说,在$apply()所有代码.$on()完成后已被触发。
th3uiguy

97

首先,一个简短的描述$on()$broadcast()$emit()

  • .$on(name, listener) -收听给定的特定事件 name
  • .$broadcast(name, args)-通过$scope所有孩子向下广播事件
  • .$emit(name, args)- $scope向所有家长(包括$rootScope

基于以下HTML(请参见此处的完整示例):

<div ng-controller="Controller1">
    <button ng-click="broadcast()">Broadcast 1</button>
    <button ng-click="emit()">Emit 1</button>
</div>

<div ng-controller="Controller2">
    <button ng-click="broadcast()">Broadcast 2</button>
    <button ng-click="emit()">Emit 2</button>
    <div ng-controller="Controller3">
        <button ng-click="broadcast()">Broadcast 3</button>
        <button ng-click="emit()">Emit 3</button>
        <br>
        <button ng-click="broadcastRoot()">Broadcast Root</button>
        <button ng-click="emitRoot()">Emit Root</button>
    </div>
</div>

触发的事件将遍历$scopes如下:

  • 广播1-仅由控制器1看到 $scope
  • 发射1- $scope然后将由控制器1看到$rootScope
  • 广播2-将由控制器2 $scope然后控制器3 看到$scope
  • 发射2- $scope然后控制器2会看到$rootScope
  • 广播3-仅由控制器3看到 $scope
  • 发射3-控制器3 然后$scope控制器2 会看到$scope$rootScope
  • 广播根-将可以看到$rootScope$scope所有的控制器(1,2,则3)
  • 发射根-只会被看到 $rootScope

使用JavaScript触发事件(同样,您可以在此处看到一个有效的示例):

app.controller('Controller1', ['$scope', '$rootScope', function($scope, $rootScope){
    $scope.broadcastAndEmit = function(){
        // This will be seen by Controller 1 $scope and all children $scopes 
        $scope.$broadcast('eventX', {data: '$scope.broadcast'});

        // Because this event is fired as an emit (goes up) on the $rootScope,
        // only the $rootScope will see it
        $rootScope.$emit('eventX', {data: '$rootScope.emit'});
    };
    $scope.emit = function(){
        // Controller 1 $scope, and all parent $scopes (including $rootScope) 
        // will see this event
        $scope.$emit('eventX', {data: '$scope.emit'});
    };

    $scope.$on('eventX', function(ev, args){
        console.log('eventX found on Controller1 $scope');
    });
    $rootScope.$on('eventX', function(ev, args){
        console.log('eventX found on $rootScope');
    });
}]);

我如何用给定的示例想象应用程序的层次结构。控制器如何成为父母或孩子??我要说的是我有一系列的状态,例如。LoginCtrl-> homeCrl-> notificationCtrl等。
HIRA THAKUR 2015年

26

您应该知道的一件事是$前缀指的是Angular方法,$$前缀指的是应避免使用的角度方法。

下面是一个示例模板及其控制器,我们将探讨$ broadcast / $ on如何帮助我们实现所需的目标。

<div ng-controller="FirstCtrl">
    <input ng-model="name"/> 
    <button ng-click="register()">Register </button>
</div>

<div ng-controller="SecondCtrl">
    Registered Name: <input ng-model="name"/> 
</div>

控制器是

app.controller('FirstCtrl', function($scope){
    $scope.register = function(){

    }
});

app.controller('SecondCtrl', function($scope){

});

我想问的是,当用户单击注册时,如何将名称传递给第二个控制器?您可能会想出多种解决方案,但是我们要使用的是使用$ broadcast和$ on。

$ broadcast vs $ emit

我们应该使用哪个?$ broadcast将引导所有子dom元素,而$ emit将引导相反的方向给所有祖先dom元素。

避免在$ emit或$ broadcast之间做出决定的最佳方法是从$ rootScope引导并对其所有子级使用$ broadcast。由于我们的dom元素是同级元素,因此这使我们的案件变得更加容易。

添加$ rootScope并让$ broadcast

app.controller('FirstCtrl', function($rootScope, $scope){
    $scope.register = function(){
        $rootScope.$broadcast('BOOM!', $scope.name)
    }
});

请注意,我们添加了$ rootScope,现在我们正在使用$ broadcast(broadcastName,arguments)。对于broadcastName,我们想给它一个唯一的名称,以便我们可以在secondCtrl中捕获该名称。我选择了BOOM!纯娱乐。第二个参数“ arguments”使我们可以将值传递给侦听器。

接收我们的广播

在第二个控制器中,我们需要设置代码以收听广播

app.controller('SecondCtrl', function($scope){
  $scope.$on('BOOM!', function(events, args){
    console.log(args);
    $scope.name = args; //now we've registered!
  })
});

真的就是这么简单。现场例子

其他获得类似结果的方法

尝试避免使用这套方法,因为它既不高效也不易于维护,但却是解决可能遇到的问题的简单方法。

通常,您可以通过使用服务或简化控制器来执行相同的操作。我们不会对此进行详细讨论,但是我想我只是为了完整性而提到它。

最后,请记住,要收听的真正有用的广播是“ $ destroy”,您可以看到$表示它是由供应商代码创建的方法或对象。无论如何,销毁控制器时会广播$ destroy,您可能想听一下,以了解何时卸下控制器。


2
作为警告,请尽量不要在您的应用中使用过多的广播/发射。跟踪这些事件的根源是一项非常艰巨的任务,尤其是在大型应用程序中,它们可能极难管理。
杨力

1
//Your broadcast in service

(function () { 
    angular.module('appModule').factory('AppService', function ($rootScope, $timeout) {

    function refreshData() {  
        $timeout(function() {         
            $rootScope.$broadcast('refreshData');
        }, 0, true);      
    }

    return {           
        RefreshData: refreshData
    };
}); }());

//Controller Implementation
 (function () {
    angular.module('appModule').controller('AppController', function ($rootScope, $scope, $timeout, AppService) {            

       //Removes Listeners before adding them 
       //This line will solve the problem for multiple broadcast call                             
       $scope.$$listeners['refreshData'] = [];

       $scope.$on('refreshData', function() {                                                    
          $scope.showData();             
       });

       $scope.onSaveDataComplete = function() { 
         AppService.RefreshData();
       };
    }); }());
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.