什么是作用域?
作用域是引用应用程序模型的对象。 它是表达式的执行上下文。 作用域以层次结构排列,模仿应用程序的DOM结构,它可以观察表达式和传播事件。
作用域的特征
Scope提供API($watch)来观察模型改变。
Scope提供API($apply),通过系统将任何模型更改传播到"AngularJS领域"(控制器,服务,AngularJS事件处理程序)外部的视图中。
Scope可以嵌套以限制对应用程序组件的属性的访问,同时提供对共享模型属性的访问。 嵌套的作用域是“子作用域”或“隔离作用域”。 “子作用域”(原型)从其父作用域继承属性。 “隔离作用域”不从父作用域中继承属性。
Scope提供对其评估表达式的上下文。 例如{{username}}表达式没有意义,除非根据定义username属性的特定作用域进行求值。
作用域作为数据模型
Scope是应用程序控制器和视图之间的粘合剂。 在模板链接阶段,指令在作用域上设置$watch表达式。 $watch允许通知属性更改的指令,这允许指令将更新的值呈现给DOM。
控制器和指令都涉及作用域,但不是互相。 这种措施将控制器与指令以及DOM隔离。 这是一个重要的点,因为它使控制器被视为不存在,这极大地改善了应用程序的测试环节。

script.js

angular.module('scopeExample',[])
.controller('MyController',['$scope',function($scope) {
  $scope.username = 'World';
  $scope.sayHello = function() {
    $scope.greeting = 'Hello ' + $scope.username + '!';
  };
}]);

index.html

<div ng-controller="MyController" ng-app="scopeExample">
  Your name:
    <input type="text" ng-model="username">
    <button ng-click='sayHello()'>greet</button>
  <hr>
  {{greeting}}
</div>

在上面的示例中,注意MyController将World指定给作用域的username属性。 作用域然后通知输入的分配,然后呈现输入用用户名预填充。 这演示了控制器如何将数据写入作用域。
类似地,控制器可以将行为分配给作用域,如sayHello方法所示,当用户单击"greet"按钮时调用。 sayHello方法可以读取username属性并创建一个greeting属性。这表明作用域上的属性在绑定到HTML的input控件时自动更新。
逻辑上{{greeting}}的渲染包括:

  • 检索与在模板中定义{{greeting}}的DOM节点相关联的作用域。在这个示例中,这是与传递到MyController的作用域相同的作用域。 (稍后我们将讨论作用域层次结构。)

  • 根据上面检索的作用域计算greeting语表达式,并将结果分配给包含的DOM元素的文本。

可以将作用域及其属性视为用于呈现视图的数据。作用域是所有视图相关的单一真实来源。
从可测试性的角度来看,控制器和视图的分离是可取的,因为它允许我们测试行为而不会被渲染细节分散注意力。

protractor.js

it('should say hello',function() {
  var scopeMock = {};
  var cntl = new MyController(scopeMock);
  // 预测用户名已预填
  expect(scopeMock.username).toEqual('World');
  // 预测我们读新的用户名和问候
  scopeMock.username = 'angular';
  scopeMock.sayHello();
  expect(scopeMock.greeting).toEqual('Hello angular!');
});

作用域层次
每个AngularJS应用程序只有一个根作用域,但可以有任意数量的子作用域。
应用程序可以有多个作用域,因为指令可以创建新的子作用域。 创建新作用域时,它们被认为是添加到父作用域的子作用域。 这创建了一个树结构,它与它们附加的DOM平行。
当AngularJS计算{{name}}时,它首先查看与name属性的给定元素相关联的作用域。 如果没有找到这样的属性,它搜索父作用域,等等,直到达到根作用域。 在JavaScript中,这种行为被称为原型继承,而子作用域原型继承自他们的父母。
此示例说明了应用程序中的作用域,以及属性的原型继承。示例后面是描述作用域边界的图。

index.html

<div class="show-scope-demo" ng-app="scopeExample">
  <div ng-controller="GreetController">
    Hello {{name}}!
  </div>
  <div ng-controller="ListController">
    <ol>
      <li ng-repeat="name in names">{{name}} from {{department}}</li>
    </ol>
  </div>
</div>

script.js

angular.module('scopeExample',[])
.controller('GreetController','$rootScope',function($scope,$rootScope) {
  $scope.name = 'World';
  $rootScope.department = 'AngularJS';
}])
.controller('ListController',function($scope) {
  $scope.names = ['Igor','Misko','Vojta'];
}]);

style.css

.show-scope-demo.ng-scope,.show-scope-demo .ng-scope  {
  border: 1px solid red;
  margin: 3px;
}

请注意,AngularJS自动将ng-scope类放置在附加了作用域的元素上。 此示例中的<style>定义以红色突出显示新作用域位置。 子作用域是必需的,因为repeater计算{{name}}表达式,但是根据表达式的作用域来计算,它会产生不同的结果。 同样,{{department}}的计算,它的作用域原型从根作用域继承,因为它是唯一定义了department属性的地方。
从DOM中检索作用域
作用域作为$scope数据属性附加到DOM,并且可以检索以用于调试目的。 (这不太可能需要在应用程序内以这种方式检索作用域。)根作用域附加到DOM的位置由ng-app指令的位置定义。 通常,ng-app放置在<html>元素上,但也可以放置在其他元素上,例如,只有一部分视图需要由AngularJS控制。
要检查调试器的作用域:

  1. 在浏览器中右键单击感兴趣的元素,然后选择“检查元素”。 应该看到浏览器调试器与你点击的元素突出显示。

  2. 调试器允许以$0变量访问控制台中当前选定的元素。

  3. 在控制台中执行检索相关联的作用域:angular.element($0).scope(),
    scope()函数仅在$compileProvider.debugInfoEnabled()为true(这是默认值)时可用。

作用域事件传播
作用域可以以类似的方式将事件传播到DOM事件。 事件可以广播到当前以及子作用域或发射到当前以及父作用域。

angular.module('eventExample',[])
.controller('EventController',function($scope) {
  $scope.count = 0;
  $scope.$on('MyEvent',function() {
    $scope.count++;
  });
}]);

index.html

<div ng-controller="EventController">
  Root scope <tt>MyEvent</tt> count: {{count}}
  <ul>
    <li ng-repeat="i in [1]" ng-controller="EventController">
      <button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
      <button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
      <br>
      Middle scope <tt>MyEvent</tt> count: {{count}}
      <ul>
        <li ng-repeat="item in [1,2]" ng-controller="EventController">
          Leaf scope <tt>MyEvent</tt> count: {{count}}
        </li>
      </ul>
    </li>
  </ul>
</div>

作用域生命周期
接收事件的浏览器的正常流程是它执行相应的JavaScript回调。一旦回调完成,浏览器重新呈现DOM并返回等待更多事件。
当浏览器调用JavaScript时,代码在AngularJS执行上下文之外执行,这意味着AngularJS不知道模型修改。为了正确处理模型修改,执行必须使用$apply方法输入AngularJS执行上下文。只有在$apply方法中执行的模型修改才会被AngularJS适当地考虑。例如,如果指令侦听DOM事件,例如ng-click,它必须计算$apply方法中的表达式。
在计算表达式之后,$apply方法执行$digest。在$digest阶段,作用域检查所有$watch表达式,并将它们与先前的值进行比较。这种脏检查是异步完成的。这意味着如$scope.username ="angular"的赋值不会立即导致$watch被通知,而$watch通知被延迟到$digest阶段。这种延迟是可取的,因为它将多个模型更新合并成一个$watch通知,以及保证在$watch通知期间没有其他$watch正在运行。如果$watch改变模型的值,它将强制额外的$digest周期。

  • 创建
    根作用域在$injector的应用程序引导期间创建。在模板链接期间,一些指令创建新的子作用域。

  • 观察者注册
    在模板链接期间,指令在作用域上注册watches。这些watches将用于将模型值传播到DOM。

  • 模型改变
    为了正确观察改变,你应该使它们只在scope.$apply()。 AngularJS API隐式执行此操作,因此在控制器中执行同步工作或使用$http,$timeout或$interval服务进行异步工作时,不需要额外的$apply调用。

  • 改变观察
    在$apply结束时,AngularJS对根作用域执行$digest循环,然后在所有子作用域中传播。在$digest周期中,检查所有被$watch监控的表达式或函数的模型改变,如果检测到改变,则调用$watch监听器。

  • 作用域销毁
    当不再需要子作用域时,子作用域创建器负责通过scope.$destroy()API销毁它们。这将停止$digest调用传播到子作用域,并允许由子作用域模型使用的内存由垃圾回收器回收。

作用域和指令
在编译阶段,编译器compiler将指令directives与DOM模板匹配。 指令通常属于两种类型之一:

  • 观察指令,例如双花括号达式{{expression}},使用$watch()方法注册监听器。 这种类型的指令需要在表达式更改时通知,以便它可以更新视图。

  • 监听器指令,例如ng-click,向DOM注册监听器。 当DOM侦听器触发时,指令执行关联的表达式并使用$apply()方法更新视图。

当接收到外部事件(例如用户操作,定时器或XHR)时,必须通过$apply()方法将关联的表达式应用于作用域,以便正确更新所有侦听器。
创建作用域的指令
在大多数情况下,指令和作用域交互,但不创建作用域的新实例。 然而,一些指令,例如ng-controller和ng-repeat,创建新的子作用域,并将子作用域附加到相应的DOM元素。
一种特殊类型的作用域是隔离作用域,它不会从父作用域继承原型。 这种类型的作用域对于应该与其父作用域隔离的组件指令非常有用。
还要注意,使用.component()帮助程序创建的组件指令始终创建隔离作用域。
控制器和作用域
作用域和控制器在以下情况下相互交互:

  • 控制器使用作用域将控制器方法暴露给模板。

  • 控制器定义可以改变模型(作用域上的属性)的方法(行为)。

  • 控制器可以在模型上注册watch。 这些监视在控制器行为执行后立即执行。

作用域$watch性能注意事项
脏检查更改作用域上的属性是AngularJS中的常见操作,因此脏检查函数必须有效。 应该注意脏检查函数不要做任何DOM访问,因为DOM访问比JavaScript对象的属性访问慢几个数量级。
作用域$watch延伸
可以使用三种策略进行脏检查:通过引用,按集合内容和按值。 策略在它们检测到的变化的种类和它们的性能特征方面不同。

  • 通过引用观察(scope.$watch(watchExpression,listener)当watch表达式返回的整个值切换到新值时检测到更改。 如果值是数组或对象,则不会检测到其中的更改。 这是最有效的策略。

  • 观察集合内容(scope.$watchCollection(watchExpression,listener))检测在数组或对象内发生的更改:添加,删除或重新排序项目时。 检测很浅 - 它不能到达嵌套集合。 观察集合内容比通过引用观察更昂贵,因为集合内容的副本需要维护。 但是,该策略尝试最小化所需的复制量。

  • 按值观察(scope.$watch(watchExpression,listener,true))检测任意嵌套数据结构中的任何变化。 它是最强大的变化检测策略,但也是最昂贵的。 每个摘要都需要完全遍历嵌套数据结构,并且需要在内存中保存它的完整副本。
    下面是示例的图例

与浏览器事件循环集成
下面的图和下面的例子描述了AngularJS如何与浏览器的事件循环交互。

  1. 浏览器的事件循环等待事件到达。事件是用户交互,定时器事件或网络事件(来自服务器的响应)。

  2. 事件的回调被执行。这将进入JavaScript上下文。回调可以修改DOM结构。

  3. 一旦回调执行,浏览器将保留JavaScript上下文并根据DOM更改重新呈现视图。

AngularJS通过提供自己的事件处理循环来修改正常的JavaScript流程。这将JavaScript分为经典和AngularJS执行上下文。只有在AngularJS执行上下文中应用的操作才能受益于AngularJS数据绑定,异常处理,属性监视等等。还可以使用$apply()从JavaScript中输入AngularJS执行上下文。请记住,在大多数地方(控制器,服务)$apply已经由处理事件的指令调用。只有在实现自定义事件回调或使用第三方库回调时,才需要显式调用 $apply。

  1. 通过调用scope.$apply(stimulusFn)进入AngularJS执行上下文,其中stimulusFn是希望在AngularJS执行上下文中执行的工作。

  2. AngularJS执行stimulusFn(),它通常修改应用程序状态。

  3. AngularJS进入$digest循环。循环由两个较小的循环组成,它们处理$evalAsync队列和$watch列表。 $digest循环继续迭代,直到模型稳定,这意味着$evalAsync队列为空,并且$watch列表未检测到任何更改。

  4. $evalAsync队列用于调度需要在当前堆栈帧之外发生的工作,但在浏览器的视图呈现之前。这通常是通过setTimeout(0)完成的,但是setTimeout(0)方法会受到缓慢的影响,并且可能会导致视图闪烁,因为浏览器在每个事件后呈现视图。

  5. $watch列表是一组自上次迭代以来可能已更改的表达式。如果检测到更改,则调用$watch函数,通常使用新值更新DOM。

  6. 一旦AngularJS的$digest循环完成,执行离开AngularJS和JavaScript上下文。随后浏览器重新呈现DOM以反映任何更改。

以下是当用户在文本字段中输入文本时,Hello world示例如何实现数据绑定效果的说明。

  1. 在编译阶段:
    1.ng-model和input指令在<input>控制上设置一个keydown监听器。
    2.插值设置一个$watch以通知名称更改。

  2. 在运行时阶段: 1.按"X"键使浏览器在输入控件上发出按键事件。 2.输入指令捕获对输入值的更改,并调用$apply("name ='X';")来更新AngularJS执行上下文中的应用程序模型。 3.AngularJS应用"name ='X';到模型。 4.$digest循环开始 5.$watch列表检测name属性的更改,并通知插值,这反过来更新DOM。 6.AngularJS退出执行上下文,这反过来退出keydown事件和它的JavaScript执行上下文。 7.浏览器使用更新的文本重新呈现视图。

Angular开发者指南六作用域的更多相关文章

  1. ios – 使用watchOS 2在Apple Watch上渲染折线图

    我正在尝试使用watchOS2在AppleWatch上渲染线条/步骤图.与iOS9不同,watchOS2不支持Quartz.它只支持CoreGraphics.我尝试编写一些代码来绘制折线图但我得到一个错误“CGContextRestoreGState:无效的上下文0x0.这是一个严重的错误.这个应用程序,或它使用的库,正在使用无效的上下文,从而有助于整体系统稳定性和可靠性降低.这个通知是礼貌的:请

  2. iOS 10 Safari问题在DOM中不再包含元素

    使用此链接,您可以重现该错误.https://jsfiddle.net/pw7e2j3q/如果您点击元素并从dom中删除它,然后单击链接测试.你应该看到旧的元素弹出选择.是否有一些黑客来解决这个问题?解决方法我能够重现这个问题.问题是,每当您尝试删除其更改事件上的选择框时,iOS10都无法正确解除对选择框的绑定.要解决此问题,您需要将代码更改事件代码放在具有一些超时

  3. performBlockAndWait在iOS 7上使用专用队列死锁的子上下文

    如果它出现在主队列中,那么你就会遇到问题,因为你正在阻止它.假设情况并非如此,您是否可以从Xcode共享显示截止日期的线程堆栈?一旦我好好看看那个堆栈,我会更新我的答案.Quellish是正确的,你没有正确插入孩子.该子MOC上的任何活动都应该在-performBlock:或-performBlockAndWait:中.我会扩展-performBlockAndWait:来覆盖对象的整个创建和决策,而不仅仅是保存.更新1-createWithAttributes是什么:inManagedobjectCont

  4. ios – 合并子上下文时的NSObjectInaccessbileExceptions

    我尝试手动重现,但失败了.是否有其他可能发生这种情况的情况,是否有处理此类问题的提示?解决方法在创建子上下文时,您可以尝试使用以下行:

  5. ios – CGContextClear警告

    解决方法我想说它肯定与CGContextClear有关.它保留了你在记忆中的所有东西.虽然您的上下文未被清除或释放,但它会将该上下文中定义的元素保留在内存中.您使用的是哪个版本的iOS?

  6. ios – 设置NSDataDetector的上下文日期

    假设今天是2014年1月20日.如果我使用NSDataDetector从“明天下午4点”字符串中提取日期,我将得到2014-01-21T16:00.大.但是,假设我希望NSDataDetector假装当前日期是2014年1月14日.这样,当我解析“明天下午4点”时,我将得到2014-01-15T16:00.如果我在设备上更改系统时间,我会得到我想要的.但是,有没有办法以编程方式指定它?

  7. 你将NSArrayController的托管对象上下文绑定到Xcode 4中的是什么?

    实际上,我对绑定一切都有同样的问题;我的UI对象都不适用于我的模型.我唯一能想到的是File的Owner和NSArrayController之间的连接出了问题.我此时还没有编写任何代码,因为我理解我不应该仅仅链接UI字段和核心数据.它是不同的因为我使用的是基于文档的应用程序?“不使用核心数据”的答案不会有成效;我知道我可以回到常规数据对象上.如果可能的话,我想在核心数据的背景下解决这个问题.TIA!

  8. ios – UIApplication.delegate必须仅在主线程中使用[复制]

    我应该在主调度中的viewControllers中声明这些)变量位置声明定义了它的范围.您需要确定这些变量的范围.您可以将它们声明为项目或应用程序级别(全局),类级别或特定此功能级别.如果要在其他ViewControllers中使用这些变量,则使用公共/开放/内部访问控制将其声明为全局或类级别.

  9. ios – 核心数据 – 保存上下文后撤消更改

    但是,实际情况并非如此!即使我使用托管对象上的更改保存上下文,以下-undo调用仍将成功撤消更改.这不是针对文档中陈述的内容吗?也许我做错了什么?

  10. ios – 我可以安全地在@try catch块中包装’CoreData无法解决错误’错误

    )是的,我偶尔会得到’CoreData无法完成故障’的错误.在我的特定应用程序中,这通常发生在一种“数据绑定”过程中,因此我可以安全地丢弃故障对象并继续前进.我想通过在@try-catch块中包装数据绑定的循环内部并且只跳过我得到CoreData错误的行来完成此操作.我可以使用CoreData安全地执行此操作吗?

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部