《AngularJS实战》——3.4 作用域的层级和事件

简介:

本节书摘来自华章出版社《AngularJS实战》一 书中的第3章,第3.4节,作者:陶国荣,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.4 作用域的层级和事件

与页面中DOM模型相似,作用域在绑定页面元素后,便依据元素的层次关系形成了自身的层级关系,而在这些层级关系中,它们还可以通过事件的传播进行数据的通信,只是这种通过事件的数据通信应用的场景非常有限,仅限于父和子级之间的作用域,接下来我们逐一进行分析。

3.4.1 作用域的层级

与DOM树状结构类似,作用域也拥有自己的层级,并且与DOM的树状结构相辅相成,它的顶级作用域只有一个,而下面的子级作用域可以创建多个,子级作用域可以继承父级作用域中的全部属性和方法,但同级别子级作用域之间却不可以互相访问各自的属性和方法。接下来,我们通过一个完整的示例来详细介绍作用域的层级关系。
示例3-8 作用域的层级
(1)功能描述
以列表的方式分组显示一个学校的两个班级学生信息,两个班级各为一个作用域,而学校则为顶级作用域,两个班级的作用域可以访问顶级作用域中的全部属性。
(2)实现代码
新建一个HTML文件3-8.html,加入如代码清单3-8所示的代码。
代码清单3-8 作用域的层级

<!doctype html>
<html ng-app="a3_8">
<head>
    <title>作用域的层级</title>
    <script src="../Script/angular.min.js"
            type="text/javascript"></script>
    <style type="text/css">
        body {
            font-size: 12px;
        }
        ul {
            list-style-type: none;
            width: 408px;
            margin: 10px 0px;
            padding: 0px;
        }
            ul .ng-scope {
                background-color: #eee;
            }
            ul li {
                f?loat: left;
                padding: 5px 0px;
            }

            ul .bold {
                font-weight: bold;
            }
            ul .school {
                f?loat: right;
                margin-right: 80px;
            }
            ul li span {
                width: 52px;
                f?loat: left;
                padding: 0px 10px;
            }
    </style>
</head>
<body>
    <div ng-controller="c3_8_school">
        <ul ng-controller="c3_8_class_1">
            <li ng-class="{{school}}">{{s_name}}{{c_name}}</li>
            <li ng-class="{{bold}}">
                <span>序号</span>
                <span>姓名</span>
                <span>性别</span>
                <span>英语</span>
                <span>数学</span>
            </li>
            <li ng-repeat=" stu in data">
                <span>{{$index+1}}</span>
                <span>{{stu.name}}</span>
                <span>{{stu.sex}}</span>
                <span>{{stu.english}}</span>
                <span>{{stu.maths}}</span>
            </li>
        </ul>
        <ul ng-controller="c3_8_class_2">
            <li ng-class="{{school}}">{{s_name}}{{c_name}}</li>
            <li ng-class="{{bold}}">
                <span>序号</span>
                <span>姓名</span>
                <span>性别</span>
                <span>英语</span>
                <span>数学</span>
            </li>
            <li ng-repeat=" stu in data">
                <span>{{$index+1}}</span>
                <span>{{stu.name}}</span>
                <span>{{stu.sex}}</span>
                <span>{{stu.english}}</span>
                <span>{{stu.maths}}</span>
            </li>    
        </ul>       
    </div>
    <script type="text/javascript">
        var a3_8 = angular.module('a3_8', []);
        a3_8.controller('c3_8_school', ['$scope',
            function ($scope) {
                $scope.s_name = "北城区试验小学";
                $scope.bold = "bold"
                $scope.school = "school"
            }]);
        a3_8.controller('c3_8_class_1', ['$scope',
            function ($scope) {
                $scope.c_name = "三年级(1)班";
                $scope.data = [
                { name: "张明明", sex: "女", english: 85, maths: 95 },
                { name: "李清思", sex: "女", english: 97, maths: 87 }
                ];
            }]);
        a3_8.controller('c3_8_class_2', ['$scope',
            function ($scope) {
                $scope.c_name = "三年级(2)班";
                $scope.data = [
                { name: "刘小华", sex: "男", english: 97, maths: 86 },
                { name: "陈忠忠", sex: "男", english: 87, maths: 88 }
                ];
            }]);
    </script>
</body>
</html>

(3)页面效果
执行HTML文件3-8.html,最终实现的页面效果如图3-8所示。

f435a3e6795db726bfc12af8151670db83bca747

(4)源码分析
在本示例源代码的控制器中,分别定义了3个控制器函数“c3_8_school”“c3_8_class_1”和“c3_8_class_2”,而在页面中通过向元素属性添加“ng-controller”指令来绑定这些函数,根据这些添加指令元素的DOM层次关系,便形成了作用域的层级关系。
在本示例作用域的层次关系中,“c3_8_school”控制器属于父级,其余两个控制器属于子级,隶属于父级,因此,它们可以直接继承父级作用域中通过“$scope”对象添加的属性或方法,即“s_name”“bold”和“school”属性,如果在父作用域中又通过“$rootScope”对象添加了属性或方法,那么子级作用域将首先访问“$scope”对象,然后再访问“$rootScope”对象。
在子级作用域的视图模板中,当页面渲染“s_name”属性值时,首先,它在取值阶段,将在元素本身所属的作用域中寻找是否存在该属性,如果不存在,则继续向上级作用域中查找,如果都没有找到,则直接在顶级的“$rootScope”对象中查找,确定属性的作用域之后,再进入计算值阶段,计算后,直接将获取的值渲染在页面的元素中。
需要说明的是,每个作用域都会自动添加一个类别名为“ng-scope”的CSS样式,因此,可以通过修改该样式,来显示各作用域所控制的范围区域。

3.4.2 作用域事件的传播

通过前面内容的介绍,我们知道,在Angular中,作用域间有非常清晰的层次结构关系,类似于DOM树状图形,最顶层的就是rootscope作用域,其余的都是在它基础之上进行分支和嵌套的。在这种关系下的作用域,它们之间的数据通信变得相对复杂,概括来说,有下列两种方式可以实现作用域的通信。
1.?服务(service)
通过在作用域间创建一个单例的服务,由该服务来处理各个作用域间的数据通信,这种方式在后续章节中介绍服务概念时,将进行详细的介绍。
2.?事件(event)
除使用服务外,还可以通过作用域间的事件进行数据通信,而要使用事件,则必须调用Angular中提供的两个方法$broadcasted和$emitted,方法$broadcasted的功能是将事件从父级作用域传播至子级作用域,它的调用格式如下。

$broadcast(eventname, data)

其中,参数eventname为定义的事件名称,data为事件传播过程中携带的数据信息。
方法$ emitted的功能是将事件从子级作用域传播至父级作用域,它的调用格式如下。

$emitted(eventname, data)

各参数的功能与$broadcasted相同,在此不再赘述。此外,除这两个传播事件的方法外,还需要通过调用$on方法,在作用域中监控传播来的事件并获取相应的数据,它的调用格式如下。

$on(eventname, function(event,data){
    // 接收传播事件的处理代码
})

在上述调用格式中,eventname为需要监控的传播事件名称,event为事件传播过程中自带的特征,该特征包括下列几个重要的属性,如表3-1所示。

3b5691acc4c20122fd899e8e164e5b5e30a7f2d4

而在$on方法处理传播事件的函数中,另外一个data参数,则为事件在传播过程中携带的数据,通过该对象可以在各个监控的作用域中获取传播时的数据,实现数据通信的功能。
虽然说通过作用域的事件可以实现数据通信的功能,但是它们的传播范围非常有限,只能是调用$broadcasted和$emitted这两个方法,在父和子级的作用域间进行传播,其他不具有这种关系的作用域将无法监控到传播来的事件。接下来,我们通过一个完整的示例来说明这点特征。
示例3-9 作用域事件的传播
(1)功能描述
在页面中添加两个功能按钮,分别执行$broadcasted和$emitted方法,进行自定义事件的传播,然后在各个作用域中通过$on方法监控事件传播的状态,并将接收后的数据显示在控制台中。
(2)实现代码
新建一个HTML文件3-9.html,加入如代码清单3-9所示的代码。
代码清单3-9 作用域事件的传播

<!doctype html>
<html ng-app="a3_9">
<head>
    <title>作用域事件的传播</title>
    <script src="../Script/angular.min.js"
            type="text/javascript"></script>
    <style type="text/css">
        body {
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div ng-controller="c3_9_p">
        <div ng-controller="c3_9_s">
            <button ng-click="to_parent()">向父级传播</button>
            <button ng-click="to_child()">向子级传播</button>
            <div ng-controller="c3_9_c"></div>
        </div>
        <div ng-controller="c3_9_b"></div>
    </div>
    <script type="text/javascript">
        var a3_9 = angular.module('a3_9', []);
        a3_9.controller('c3_9_s', function ($scope) {
            $scope.to_parent = function () {
                $scope.$emit('event_1', '事件来源于子级');
            }
            $scope.to_child = function () {
                $scope.$broadcast('event_2', '事件来源于父级');
            }
        });
        a3_9.controller('c3_9_p', function ($scope) {
            $scope.$on('event_1', function (event, data) {
                console.log('在父级中监听到', data);      
            });
            $scope.$on('event_2', function (event, data) {
                console.log('在父级中监听到', data);   
            });
        });
        a3_9.controller('c3_9_c', function ($scope) {
            $scope.$on('event_1', function (event, data) {
                console.log('在子级中监听到', data);   
            });
            $scope.$on('event_2', function (event, data) {
                console.log('在子级中监听到', data); 
            });
        });
        a3_9.controller('c3_9_b', function ($scope) {
            $scope.$on('event_1', function (event, data) {
                console.log('在同级中监听到', data);   
            });
            $scope.$on('event_2', function (event, data) {
                console.log('在同级中监听到', data);  
            });
        });
    </script>
</body>
</html>

(3)页面效果
执行HTML文件3-9.html,最终实现的页面效果如图3-9所示。

d077011194c9c448062228dad6dfc82abd52d05b

(4)源码分析
在本示例的JavaScript代码中,定义了多个控制器,并通过ng-controller指令将它们与页面中的各个作用域相绑定,在其中名为“c3_9_s”的控制器中,添加了两个方法to_parent和to_child,分别用于在单击页面中两个按钮时调用。
在to_parent方法中,直接调用Angular中的$emit方法,向父作用域传播“event_1”事件和“事件来源于子级”的字符串数据,而在to_child方法中,则是调用Angular中的$broadcast方法,向子作用域传播“event_2”事件和“事件来源于父级”的字符串数据;在其他的控制器中,则是通过调用$on方法接收其他作用域传播来的事件和数据,并将数据显示在浏览器控制台中。
虽然除“c3_9_s”的控制器外,都通过$on方法接收其他作用域传播来的事件和数据,但当用户在页面中单击第一个按钮时,只有父级作用域才接收到了子作用域传播来的“event_1”事件和相应字符串内容,其他作用域都没有接收到。因此,只在浏览器控制台中显示“在父级中监听到事件来源于子级”的字样,效果如示意图3-9所示。
当用户单击页面中的第二个按钮时,也只有在子级作用域才接收到了父级作用域传播来的“event_2”事件和相应字符串内容,其他作用域同样也都没有接收到,通过这个示例,我们清楚地看到,通过作用域中的事件传播数据的功能非常有限,只能调用Angular中的$emit和$broadcast方法,在父级和子级作用域中进行事件数据的传递,这点在代码开发时需要注意。

相关文章
|
15天前
|
JavaScript 测试技术 数据处理
AngularJS中的控制器:作用与使用技术详解
【4月更文挑战第27天】AngularJS的控制器作为视图和模型间的桥梁,负责数据绑定、逻辑处理和初始化状态。它们与视图关联,通过`$scope`共享数据和方法。最佳实践包括保持控制器简洁、谨慎使用`$scope`、遵循单一职责原则和编写测试。通过理解控制器的作用和使用,能构建动态响应的应用。
|
15天前
|
JavaScript
AngularJS中的自定义指令:创建与使用技术详解
【4月更文挑战第27天】本文详细介绍了AngularJS中自定义指令的创建与使用。通过定义指令工厂函数并注册到模块中,可以创建自定义指令,如示例中的`myCustomDirective`。指令的属性(如`restrict`、`template`、`replace`)和方法(如`link`、`scope`)可定制其行为。在HTML中使用`restrict`指定的方式(如元素、属性等)来插入指令。遵循命名规范,避免直接DOM操作,使用隔离作用域和关注重用性与扩展性,能有效提升代码质量。自定义指令是AngularJS强大功能之一,有助于实现复杂DOM操作和组件复用。
|
5月前
|
前端开发 JavaScript 开发者
详细介绍 AngularJS 表单的各种特性、用法和最佳实践
详细介绍 AngularJS 表单的各种特性、用法和最佳实践
79 1
|
JavaScript 容器 数据挖掘
Angularjs 与三方js插件配合使用,并通过模板动态解析angularjs 语法
在一个静态见面上做数据分析,由于前后端分离 前端使用Angularjs框架,后端使用RESTFUL,如图
2662 0
|
Web App开发 JavaScript 前端开发
|
JavaScript 前端开发 移动开发
|
Web App开发 JavaScript 前端开发