5.4 作用域事件路由与广播

AngularJS作用域的另外一个优秀特性是它的事件传播机制,在一些情况下,我们需要在控制器中对一些事件做出处理。例如Ajax请求完成事件,我们需要通知控制器处理服务端返回的数据,作用域事件传播机制可以帮我们完成这些操作。除此之外,当我们需要在控制器直接传递数据时,也可以使用作用域事件机制来完成。

如图5.5所示,AngularJS作用域支持下面两种事件传播方式:

● 事件从子作用域路由到父作用域中。

● 事件从父作用域广播到所有子作用域中。

图5.5 AngularJS作用域事件传播机制

与AngularJS作用域事件相关的方法有$on()、$emit()、$broadcast(),接下来以实际案例对作用域两种事件传播机制进行介绍。

5.4.1 $emit方法实现事件路由

AngularJS作用域对象提供了一个$emit()方法,用于实现事件从子作用域路由到父作用域中,$emit()方法第一个参数为事件名称,后面可以传入一个或多个参数,这些参数能够被传递到父作用域注册的事件监听器中,$emit()方法使用如下:

        $scope.$emit("infoEvent", {name:"Jane", age:23});

消息发送出去以后,我们可以在父作用域中调用AngularJS作用域对象的$on()方法,注册一个事件监听器监听子作用域路由的事件,下面是一个完整的作用域事件路由案例:

代码清单:ch05\ch05_13.html

        <!doctype html>
        <html ng-app=”eventModule”>
        <head>
            <meta charset=”UTF-8”>
            <title>ch05_13</title>
            <script type=”text/javascript” src=”../angular-1.5.5/angular.js”>
            </script>
            <style>
                #parent{
                    width: 350px;
                    height: 250px;
                    border: 3px solid #ccc;
                }
                #child{
                    width: 300px;
                    height: 200px;
                    border: 3px solid #ccc;
                    margin: 10px auto;
                }
            </style>
        </head>
        <body>
            <div id=”parent” ng-controller=”ParentController”><! --父级作用域-->
                父作用域
                <div id=”child” ng-controller=”ChildController”><! --子级作用域-->
                    子作用域
                    <button ng-click=”postEvent()">Emit</button>
                </div>
            </div>
            <script>
                var app = angular.module('eventModule', [])
                app.controller('ParentController',
                    function($scope){
                        $scope.$on("infoEvent”, function(event, data){
                            console.log("接收到子作用域事件...”);
                            console.log(data);
                        });
                    });

                app.controller('ChildController',
                    function($scope){
                        $scope.postEvent = function(){
                            $scope.$emit("infoEvent”, {name:”Jane”, age:23});
                        }
                    });

            </script>
        </body>
        </html>

在浏览器中预览ch05_13.html页面,效果如图5.6所示,单击Emit按钮后,父作用域成功接收到子作用域路由的消息。

图5.6 作用域事件路由案例

在本案例中,我们通过两个嵌套ng-controller实例化两个控制器对象,控制器对象实例化时会创建两个具有父子关系的作用域对象。

接着对Emit按钮进行事件绑定,单击按钮时调用postEvent()方法,在postEvent()方法中调用子作用域对象的$emit()方法向父作用域路由事件,事件名称为infoEvent,第二个参数为向事件监听器传递的数据,这里我们传递了一个JavaScript对象。

在ParentController控制器中,调用父作用域的$on方法声明一个事件监听器,监听事件名称为infoEvent。$on方法的第二个参数为事件监听器定义部分,它接收两个参数,第一个参数为事件对象,第二个参数为子作用域中传递的数据,在事件监听器中输出一些信息。

打开浏览器开发人员工具,当我们单击Emit按钮时,ParentController控制器中声明的监听器能够监听到infoEvent事件,并向控制台输出信息。

这是第一种事件传播方式,5.4.2小节将介绍从父作用域到子作用域的事件广播机制。

5.4.2 $broadcast方法实现事件广播

$broadcast()方法的使用和$emit()方法相同,不同的是,它用于向子作用域广播事件,所有的子作用域只要注册了事件监听器就能收到父作用域的广播事件。下面是一个事件广播的具体案例:

代码清单:ch05\ch05_14.html

    <!doctype html>
    <html ng-app="eventModule">
    <head>
        <meta charset="UTF-8">
        <title>ch05_14</title>
        <script type="text/javascript" src="../angular-1.5.5/angular.js">
        </script>
        <style>
            #parent{
                width: 450px;
                height: 250px;
                border: 3px solid #ccc;
            }
            .child{
                width: 150px;
                height: 200px;
                border: 3px solid #ccc;
                float: left;
                margin-left: 20px;
            }
        </style>
    </head>
    <body>
        <div id="parent" ng-controller="ParentController"><! --父级作用域-->
            <div>父作用域
            <button ng-click="postEvent()">Broadcast</button>
            </div>
            <div class="child" ng-controller="Child1Controller"><! --子级作用域-->
                子作用域1
            </div>
            <div class="child" ng-controller="Child2Controller"><! --子级作用域-->
                子作用域2
            </div>
        </div>
        <script>
            var app = angular.module('eventModule', [])
            app.controller('ParentController',
                function($scope){
                    $scope.postEvent = function() {
                        $scope.$broadcast("infoEvent", {name:"Jane", age:23});
                    }
                });
            app.controller('Child1Controller',
                function($scope){
                    $scope.$on("infoEvent", function(event, data){
                        console.log("子作用域1接收到父作用域广播事件...");
                        console.log(data);
                    });
                });
                app.controller('Child2Controller',
                    function($scope){
                        $scope.$on("infoEvent", function(event, data){
                            console.log("子作用域2接收到父作用域广播事件...");
                            console.log(data);
                        });
                    });
            </script>
        </body>
        </html>

在浏览器中运行ch05_14.html页面,效果如图5.7所示,当我们单击Broadcast按钮后,控制台输出内容如下:

        子作用域1接收到父作用域广播事件...
        Object {name: "Jane", age: 23}
        子作用域2接收到父作用域广播事件...
        Object {name: "Jane", age: 23}

分析控制台输出的日志,结合案例代码可知,两个子作用域同时接收到父作用域的广播事件。

图5.7 作用域事件广播案例

在上面的代码中,在ParentController控制器作用范围内通过两个ng-controller指令实例化Child1Controller和Child2Controller控制器,AngularJS在实例化这两个控制器时会创建两个平级的子作用域对象。

在ParentController控制器构造方法中响应Broadcast按钮的单击事件,在事件处理方法postEvent()中调用父作用域对象的$broadcast()方法向子作用域广播事件。

接着在Child1Controller和Child2Controller两个控制器中调用子作用域对象的$on()方法分别注册事件监听器对广播事件进行处理。

当我们单击Broadcast按钮时,打开浏览器开发人员工具,可以看到两个子作用域中都能接收到父作用域广播事件。

5.4.3 作用域对象$on方法详解

前两个小节的案例中,我们调用作用域对象的$emit()和$broadcast()方法把事件传播出去后,使用到作用域对象的$on方法,本小节将对该方法做更加详细的介绍。

$on()方法用于注册一个事件监听器,该方法接收两个参数,第一个参数是要监听事件的名称,第二个参数是事件处理方法,具体使用如下:

        $scope.$on("infoEvent", function(event, data){
        });

事件处理方法的第一个参数event为事件对象,第二个参数data为调用$emit()或$broadcast()方法传递的数据。需要注意的是,event事件对象具有一些实用的属性和方法,我们能够通过它获取更多关于事件的信息,具体如下:

● event.name:事件的名称。

● event.targetScope:事件源作用域对象。

● event.currentScope:当前作用域对象。

● event.stopPropagation():这个方法用于停止事件的进一步传播。需要注意的是,该方法只对向父作用域路由事件起作用,当在某个事件监听处理方法中调用事件对象的stopPropagation()方法后,事件将不会再向上级父作用域路由。它对调用$broadcast()方法广播的事件不起作用。

● event.preventDefault():这个方法实际上不会做什么操作,但是会设置defaultPrevented属性为true,直到事件监听器的实现者采取行动之前才会检查defaultPrevented的值。

● event.defaultPrevented:如果调用了event.preventDefault()方法,那么该属性将被设置为true。