05作用域-AngularJS基础教程

0. 目录

  • 目录
  • 前言
  • 正文
    • 1 setup
    • 2 scope
    • 3 rootScope
    • 4 Isolation隔离
    • 5 Nesting嵌套
    • 6 Inheritance继承
    • 7 scopewatch
    • 8 scopeapply
    • 9 结论
  • 声明

1. 前言

AngularJS是为了克服HTML在构建应用上的不足而设计的。HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足。

AngularJS的初衷是为了简化web APP开发,精髓是简单。但是国内外有很多AngularJS的教程,都着重讲AngularJS的强大之处,从而增加了AngularJS学习的难度,本教程试图用通俗的语言讲解最为基础最为实用的内容,简化学习进程、降低学习难度是本教程的初衷。

本系列教程以翻译Chris SmithAngualr Basics为梗概,融合博主自己的理解,为大家提供一个简单明了的学习教程。

本文为系列教程第5篇作用域,翻译自Scope

  1. 引言-Introduction
  2. 基础-Basics
  3. 控制器-Controllers
  4. 作用域-Scopes
  5. 集合-Collections
  6. 模块-Modules
  7. 依赖注入-Dependency Injection
  8. 服务-Services
  9. 过滤器-Filters
  10. 指令-Directives
  11. 指令作用域-Directive Scopes
  12. 路由-Routing
  13. 通信-HTTP
  14. 结论

2.正文

在Angular中scope到底是什么?从名字来看,你可能猜测它是表征程序状态的上下文,保护我们不受JS饱受诟病的全局作用域污染。听起来很简单,我们好像可以直接跳到下一章了。

不要太急,本章虽说不长,但是会涉及一些非常重要的内容:scope继承和体系。一个经典的Angular应用可能会创建10几个、上百个甚至近千个scope,这些scope形成一个体系。

在我们更进一步之前,让我们先搭建本章案例的环境。

2.1 setup

基础章,我们学会了通过向html元素添加ng-app指令告诉Angular需要处理那部分文档。

<!-- index.html -->
<body ng-app="app">
  <!-- Other examples to be inserted here. -->
</body>

ng-app指令的参数就是我们应用的根模块(本案例命名为app,只是为了简单起见)。Angular的模块我们会在下一张详细介绍。现在,我们只需考虑这个启动样板文件(也可以先暂时忽略掉)。

/* module.js */
angular.module(‘app‘, []);
angular.module(‘app‘).config([‘$controllerProvider‘, function($controllerProvider) {
  $controllerProvider.allowGlobals();
}]);

现在让我们把这些放到一边,来点实用的。

2.2 $scope

上一章,我们学习了如何在$scope引用上附加属性的方式准备模型,让我们重复下练习。

/* name-controller.js */
function NameController($scope) {
  $scope.name = "First";
}

使用ng-controller指令,我们可以在DOM元素环境中调用控制器函数。我们在控制器中指定给scope的任意数据都在p元素内可用。

<!-- name-controller.html -->
<p ng-controller="NameController">
  {{name}}
</p>

编译结果为: First

我们调用NameController元素之外的元素,不能访问该控制器的scope。我们来测试一下。

<!-- name-controller-nested.html -->
<div>
  <p>
    Outside the scope: {{name}}
  </p>
  <div ng-controller="NameController">
    <p>
      Inside the scope: {{name}}
    </p>
  </div>
</div>

编译结果为:

Outside the scope:

Inside the scope: First

现在我们明白了scope和controller相匹配。接下来,我们来看下相反的情况:只有一个scope的Angular应用。

2.3 $rootScope

为了帮助我们避免scope使用中可能出现的麻烦,Angular为每一个控制器创建一个新的scope。但是,scope是层次结构的,在每个应用scope层次结构最底端的根scope是单一的。我们可以通过在控制器中声明特定的参数$rootScope访问它,最好不要使用普通的$scope引用。

/* root-name-controller.js */
function RootNameController($rootScope) {
  $rootScope.name = "First";
}

看起来有点天真,实际上它可以正常工作。

<!-- root-name-controller.html -->
<p ng-controller="RootNameController">
  {{name}}
</p>

编译结果为:First.

但是,当我们在另一个控制器里给它指定相同的属性时,就出问题了。

/* second-root-name-controller.js */
function SecondRootNameController($rootScope) {
  $rootScope.name = "Second";
}

这个不对,因为$rootScope在我们的应用中作为单体存在,所以我们只能给它指定一个值。

<!-- root-name-controllers.html -->
<p ng-controller="RootNameController">
  {{name}}
</p>
<p ng-controller="SecondRootNameController">
  {{name}}
</p>

编译结果为:

Second

Second

实际上,我们的SecondRootNameController中指定的name值覆盖了RootNameController中的值,所以这个问题就是全局变量的问题,是吧?

2.4 Isolation(隔离)

通过自动为每个控制器,Angular可以为我们提供非常安全的环境,让我们重新控制器使用正确的方式发布模型,弃用$rootscope而使用$scope注意一点,上文我们的$rootscope的案例仅仅是为了演示为什么每一个控制器自动生成一个scope对象。

/* second-name-controller.js */
function SecondNameController($scope) {
  $scope.name = "Second";
}

使用本章开头的NameController和上面的SecondNameController,我们来演示下$scope对象的隔离性。

<!-- second-name-controller.html -->
<p ng-controller="NameController">
  {{name}}
</p>
<p ng-controller="SecondNameController">
  {{name}}
</p>

编译结果为:

First

Second

该案例产生了正确的结果,每个控制器输出了自己的名字。当一个控制器不是另一个控制器的子元素,产生了隔离性(isolation)。如果是嵌套的情况,又会发生什么呢?

2.5 Nesting(嵌套)

我们稍微修改上面的案例,把第二个控制器SecondNameController移到加载NameController的div元素的子元素上,来看看嵌套的时候会发生什么。

<!-- nested-second-name-controller.html -->
<div ng-controller="NameController">
  <p>
    {{name}}
  </p>
  <p ng-controller="SecondNameController">
    {{name}}
  </p>
</div>

编译结果为:

First

Second

依然能够正常工作。如果我们改变两个p的位置会怎么样,不妨尝试一下,输出结果也会翻转一下,是吧。

好像看起来嵌套的控制器依然可以相互隔离,但其实是个误导。实际上,Angular基于DOM中的相对位置组织控制器的层次结构,嵌套的控制器会继承祖先的属性。我们这里没有变化的原因是,子scope的name属性覆盖了父scope的name属性。

让我们改变一下子scope的属性名称,来试试看scope嵌套中的继承特性。

/* child-controller.js */
function ChildController($scope) {
  $scope.childName = "Child";
}

我们在父元素和子元素中分别使用两个属性。

<-- child-controller.html -->
<div ng-controller="NameController">
  <p>
    {{name}} and {{childName}}
  </p>
  <p ng-controller="ChildController">
    {{name}} and {{childName}}
  </p>
</div>

编译结果为:

First and

First and Child

这样就很明了了,父控制器不能访问子控制器的属性,但是子控制器可以访问自己的属性和父控制器的属性。

因为name是继承的属性,好像我们可以在父scope和子scope中同时看到它的变化,事实是不是这样?我们给父控制器绑定个input来测试下。

<!-- child-controller-edit.html -->
<div ng-controller="NameController">
  <p>
    {{name}}
  </p>
  <p ng-controller="ChildController">
    {{name}}
  </p>
  <input type=‘text‘ ng-model=‘name‘>
</div>

编译结果如下动图所示。

如果您尝试编辑name属性,您将会看到期望的结果,如上动图所示。name属性会在两个scope环境内更新。但是,注意我们这里把name绑定在父scope上。

2.6 Inheritance(继承)

您可能觉得我们修改子scope属性会同步更新到父scope中,是这样的吗?让我们测试下,在两个控制器中增加input。

在下面的案例中,第一步修改头一个input,第二个修改第二个input,看看结果是不是你想的那样。

<!-- child-controller-input.html -->
<div ng-controller="NameController">
  <p>
    name: {{name}}
    <br>
    <input type=‘text‘ ng-model=‘name‘>
  </p>
  <p ng-controller="ChildController">
    name: {{name}}
    <br>
    <input type=‘text‘ ng-model=‘name‘>
  </p>
</div>

编译结果如下动图所示。

Angular使用JS的普通原型继承,某种程度上来说不错,因为我们无需学习新的东西,某种程度上又不好,因为JS的原型继承不太直观。

对JS对象设置属性导致对象该属性的自动生成,对于属性继承来说不是好消息,因为它将覆盖对象自己的属性。

哈!简单的说,如果您没有修改第二个input,子scope中没有name属性。一旦您修改了input,自动在子scope建立name属性,赋input的值,并且阻断对父scope中name属性的访问。

明白了吗?不明白的话,可以稍微休息下,去学习下原型继承。如果明白了,我们继续。

如果我们想在继承的scope中修改模型数据,我们该如何做?

很简单,我们只需要把name属性移动另一个对象上。

/* info-controller.js */
function InfoController($scope) {
  $scope.info = {name: "First"};
}
/* child-info-controller.js */
function ChildInfoController($scope) {
  $scope.info.childName = "Child";
}
<!-- info-controller.html -->
<div ng-controller="InfoController">
  <p>
    {{info.name}} and {{info.childName}}
    <br>
    <input type=‘text‘ ng-model=‘info.name‘>
  </p>
  <p ng-controller="ChildInfoController">
    {{info.name}} and {{info.childName}}
    <br>
    <input type=‘text‘ ng-model=‘info.name‘>
  </p>
</div>

编译之后结果动图所示。

注意ChildInfoController依赖于它的父控制器创建info对象。 如果您编辑ChildInfoController的源文件,把函数体替换为$scope.info = {childName: "Second"};会发生什么呢?尝试一下。

function InfoController($scope) {
  $scope.info = {name: "First"};
}
function ChildInfoController($scope) {
  $scope.info = {childName: "Second"};
}
<div ng-controller="InfoController">
  <p>
    {{info.name}} and {{info.childName}}
    <br>
    <input type=‘text‘ ng-model=‘info.name‘>
  </p>
  <p ng-controller="ChildInfoController">
    {{info.name}} and {{info.childName}}
    <br>
    <input type=‘text‘ ng-model=‘info.name‘>
  </p>
</div>

2.7 scope.$watch

大部分时候,Angular的双向绑定可以像您期望的那样完成交互: 当您使用input发生改变时,UI也会同步的发生更改。但是,计算属性(指从其它scope数据中拿到的数据),例如下面案例中所示的sum就是一个计算属性,不是那么一回事儿。

/* sum-controller.js */
function SumController($scope) {
  $scope.values = [1,2];
  $scope.newValue = 1;
  $scope.add = function() {
    $scope.values.push(parseInt($scope.newValue));
  };

  // Broken -- doesn‘t trigger UI update
  $scope.sum = $scope.values.reduce(function(a, b) {
    return a + b;
  });
}

本例的模板文件中,如下所示,我们使用select表单让用户选择数据(1,2,3)添加到数组的末尾。(顺便说一句,控制器给新的值提供了初始值,否则Angular应该为select元素添加空白option以避免武断地给新值赋予第一个option。这个行为对scope用处不大,但是我们有必要了解一下)

<!-- sum-controller.html -->
<p ng-controller="SumController">
  <select ng-model="newValue" ng-options="n for n in [1,2,3]"></select>
  <input type="button" value="Add" ng-click="add()">
  The sum of {{values}} is {{sum}}.
</p>

单击add按钮可以把数字添加到数组里去,但是,悲催的是,没有正确的反应到sum里去。

让我们来做点修正,把计算值得部分移到一个回调函数里去。将这个回调函数(具备一个watchExpression参数,本案例就是要被计算的属性)作为参数传递给$scope.$watch,当属性发生改变时调用计算函数求和。

/* sum-watch-controller.js */
function SumController($scope) {
  $scope.values = [1,2];
  $scope.newValue = 1;
  $scope.add = function() {
    $scope.values.push(parseInt($scope.newValue));
  };
  $scope.$watch(‘values‘, function () {
    $scope.sum = $scope.values.reduce(function(a, b) {
      return a + b;
    });
  }, true);
}

可以看到,求和的函数就会随着变量的变化发生作用了。

2.8 scope.$apply

Angular内置的双向绑定指令已经很牛了,但是,我们也经常时不时的有些需要添加的行为。比如,如果您想让用户通过esc键同时清除文本框当前状态和scope中的绑定状态。我们应该如何编写这个自定义事件?

<!-- escape-controller.html -->
<div ng-controller="EscapeController">
  <input type="text" ng-model="message">
  is bound to
  "<strong ng-bind="message"></strong>".
  Press <code>esc</code> to clear it!
</div>

首先,我们需要在控制器中声明一个特定名字的参数$element,以便于Angular建立一个关联DOM的引用。使用给定元素的bind方法,我们可以注册一个回调函数,侦听keyup事件。在该回调函数中,我们更新scope属性。简单吧,我们试一下,试着输入点东西,然后按esc键。

/* escape-controller.js */
function EscapeController($scope, $element) {
  $scope.message = ‘‘;
  $element.bind(‘keyup‘, function (event) {
    if (event.keyCode === 27) { // esc key

      // Broken -- doesn‘t trigger UI update
      $scope.message = ‘‘;
    }
  });
}

没有正常工作吧。因为我们这里使用Dom,我们需要告诉Angular什么时候重新渲染视图。我们把需要改变的内容打包成一个回调函数传递给$scope.$apply

/* escape-apply-controller.js */
function EscapeController($scope, $element) {
  $scope.message = ‘‘;
  $element.bind(‘keyup‘, function (event) {
    if (event.keyCode === 27) { // esc key

      $scope.$apply(function() {
        $scope.message = ‘‘;
      });
    }
  });
}

这样就可以正常工作了吧。

2.9 结论

如果您试图让Angular适应于MVC模式,scopes会是一个难题。刚开始非常简单,Scopes是模型层的一部分,在Angular中,一个对象直到能够作为scope的属性可以访问时,才成为模型。但是,当您研究scopes如何通过控制器或者指令绑定到DOM时,这些东西将变得很有意思(我们后头再说)。幸运的是,排除这些学术问题外,如上面案例所示,scopes非常直观、简单易用。

3.声明

前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖!

欢迎任何形式的转载,烦请注明装载,保留本段文字。

本文原文链接,http://blog.csdn.net/whqet/article/details/44925881

欢迎大家访问独立博客http://whqet.github.io

时间: 2024-10-27 17:49:16

05作用域-AngularJS基础教程的相关文章

转: angularjs基础教程(~~较通俗)

Angularjs-基础教程(1) 很久没有写过东西了,感觉写东西都不知道从哪里开始了,现在还是先写点技术性的吧,angularjs--我兄弟把它叫成“俺哥啦js” 1.下载 官方网址:https://angularjs.org/ CDN:https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js 2.简单介绍使用 1.ng-app   ~~~angularjs的渲染范围 决定了angularjs的作用域

03基础-AngularJS基础教程

0. 目录 目录 前言 正文 1 Set up 2 表达式 3 指令 ng-bind ng-init ng-non-bindable ng-show 4 作用域 双向绑定Two-way binding ng-model 5 总结 声明 1. 前言 AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,所以AngularJS做了一些工作来来解决静态网页技术在构建动态应用上的不足. AngularJS

04控制器-AngularJS基础教程

0. 目录 目录 前言 正文 1 ng-controller 2 构建模型-Constructing the model 3 控制器作为属性名-Controller as propertyName 4 依赖注入-Dependency injection 41 scope 5 模型视图控制器MVC-Model-view-controller 6 函数-Functions 7 回调函数 8 结论 声明 1. 前言 AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为

07模块化-AngularJS基础教程

0. 目录 目录 前言 正文 1 Why use Angular modules为什么使用模块化 2 创建应用模块-Creating the application module 3 加载应用模块-Loading the application module 4 定义控制器-Defining a controller 5 链式定义-Chaining definitions 6 加载模块-Loading modules 10 结论 声明 1. 前言 AngularJS是为了克服HTML在构建应用上

06集合-AngularJS基础教程

0. 目录 目录 前言 正文 1 Set up 2 Iteration 3 ng-repeat 4 Object properties 5 index 6 ng-init 7 Uniqueness 71 严格相等 72 track by 8 Callback functions 9 -start and -end 10 结论 声明 1. 前言 AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了,

Angular系列----AngularJS入门教程05:双向绑定(转载)

在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f step-4 你应该发现除了搜索框之外,你的应用多了一个下来菜单,它可以允许控制电话排列的顺序. 步骤3和步骤4之间最重要的不同在下面列出.你可以在GitHub里看到完整的差别. 模板 app/index.html Search: <input ng-model="query"&g

Python基础教程(第九章 魔法方法、属性和迭代器)

本文内容全部出自<Python基础教程>第二版,在此分享自己的学习之路. ______欢迎转载:http://www.cnblogs.com/Marlowes/p/5437223.html______ Created on Marlowes 在Python中,有的名称会在前面和后面都加上两个下划线,这种写法很特别.前面几章中已经出现过一些这样的名称(如__future__),这种拼写表示名字有特殊含义,所以绝不要在自己的程序中使用这样的名字.在Python中,由这些名字组成的集合所包含的方法称

Python基础教程(第十章 自带电池)

本文内容全部出自<Python基础教程>第二版,在此分享自己的学习之路. ______欢迎转载:http://www.cnblogs.com/Marlowes/p/5459376.html______ Created on Marlowes 现在已经介绍了Python语言的大部分基础知识.Python语言的核心非常强大,同时还提供了更多值得一试的工具.Python的标准安装中还包括一组模块,称为标准库(standard library).之前已经介绍了一些模块(例如math和cmath,其中包

SQL Server2012 T-SQL基础教程--读书笔记(5-7章)

SQL Server2012 T-SQL基础教程--读书笔记(5-7章) SqlServer T-SQL 示例数据库:点我 Chapter 05 表表达式 5.1 派生表 5.1.1 分配列别名 5.1.2 使用参数 5.1.3 嵌套 5.1.4 多个引用 5.2 公用表表达式 5.2.1 分别列别名 5.2.2 使用参数 5.2.3 定义多个CTE 5.2.4 CTE的多次引用 5.2.5 递归CTE 5.3 视图 5.3.1 视图和ORDER BY 子句 5.3.2 视图选项 5.4 内嵌表