转载请写明来源地址:http://www.jb51.cc/article/p-ckoromyb-bph.html

Scopes简介

Scopes是一个指向application模型的对象,是表达式执行的上下文,模拟application的DOM结构构成自己的层次结构。Scope可以观察表达式和传播事件。

Scopes特点

Scopes提供了API $watch观察model的变化。
Scopes提供了API $apply通过系统把model的更改从“Angular realm”(controllers,services,Angular event handlers)外部传播到view之中。
Scopes形成嵌套限制访问application组件的属性,同时也可以访问一些共享model的属性。所谓嵌套scopes不外乎两种,一种child scopes,另一种isolate scope,child scope可以访问从父scope继承的属性,isolate scope则不可以。
Scopes提供一个上下文,在其中的表达式可以求值。例如{ { username } }表达式是没有意义的,但是看做是在scopes上下文中的表达式的求值的话就有意义了。

Scope作为Data-Model

Scope是Controller和view之间的粘合剂,在程序链接阶段directives在scopes中设置$watch表达式,$watch允许directives得到属性改变的通知,并允许directives将改变后的值呈现给DOM视图。
Controller和directives都和scopes相关联,但是相互直接又不直接关联。这样使得Controller从指令和视图中隔离开来。这一点很重要,这使得Controller和view相互不可知,极大的提高了程序的可测试性。

<!DOCTYPE html> <html> <head> </head> <script src="script/angular.min.js"></script> <script> angular.module('scopeExample',[]) .controller('MyController',['$scope',function($scope) { $scope.username = 'World'; $scope.sayHello = function() { $scope.greeting = 'Hello ' + $scope.username + '!'; }; }]); </script> <body> <div ng-app="scopeExample" ng-controller="MyController"> Your name: <input type="text" ng-model="username"> <button ng-click='sayHello()'>greet</button> <hr> {{greeting}} </div> </body> </html>
在上面的例子中,MyController首先将World赋值给scope中的username属性,scope将通知input这次赋值,然后将username的值预先填入到输入框中,这里演示了controller怎么样将数据写入到scope中。 同样controller可以分配行为给scope,比如这个例子中的sayHello方法,当用户点击greet按钮时,sayHello方法就会被调用。sayHello方法会去读取username属性并创建一个greeting属性。这里演示的是绑定到input部件的属性的自动更新。 greeting属性的呈现逻辑上分为两步: 1.在DOM中的模板里定义了{{greeting}},首先检索DOM关联的scope。 2.针对以上检索到的scope,计算求值greeting,并将结果分配给这个enclosing DOM元素的文本。 你可以把scopes及其属性是用于呈现view的数据,而且是view相关的单一真实数据来源。

Scope层次结构

每个angular的application都有一个root scope,但是可以有多个child scopes。 application可以有多个scopes,因为一些directives可以创建新的child scopes,新的scopes一旦被创建,就会被作为child scope增加到它的parent scope中,和他们链接的DOM一样,构成一个树形结构。 当Angular对{{name}}属性求值时,先在相关联的scope内寻找这个属性,如果没找到它就会去parent scope中找,一直搜到root scope。在JavaScript中这种行为被称为prototypical inheritance,child scopes prototypically继承于他们的parent scope。 这个例子讲解了application中点scopes,还有属性的prototypical inheritance,例子后面的一张图描诉了scope的边界。
<!DOCTYPE html> <html> <head> </head> <script src="script/angular.min.js"></script> <script> angular.module('scopeExample',[]) .controller('GreetController','$rootScope',function($scope,$rootScope) { $scope.name = 'World'; $rootScope.department = 'Angular'; }]) .controller('ListController',function($scope) { $scope.names = ['Igor','Misko','Vojta']; }]); </script> <style> .show-scope-demo.ng-scope,.show-scope-demo .ng-scope { border: 1px solid red; margin: 3px; } </style> <body ng-app="scopeExample"> <div class="show-scope-demo"> <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> </body> </html>
![这里写图片描述](http://img.blog.csdn.net/20161125170052951) ![这里写图片描述](http://img.blog.csdn.net/20161125170115787) 注意到Angular自动将scope影响到的元素附上了ng-scope的class样式。style定义了在新的scope创建的地方都会红色高亮显示。{{name}}在不同的scope内会得出不同的值,{{department}} prototypically继承于root scope。

在DOM中检索Scopes

Scope作为一种数据属性附加到DOM中,可以用来调试目的的检索。root scope被附加到ng-app声明的DOM节点。通常ng-app放在元素,但它可以放在其他元素,例如,只有部分视图需要由Angular控制。 在调试器中检查scope: 1.在浏览器中右键你感兴趣的元素,并且选择检查元素(推荐chrome浏览器),您应该看到浏览器调试器与你点击高亮显示的元素。 2.调试器允许您访问当前选中的元素在Console输入 $0 3.在Console中执行angular.element( $0).scope()来检索scope

Scope事件传播

Scopes也能传播事件,就像传播DOM事件一样,可以通过broadcast广播到子children scopes,通过emit扩散到parent scope
<!DOCTYPE html> <html ng-app="eventExample"> <head> <Meta charset="uft-8"/> <title></title> </head> <script src="script/angular.min.js"></script> <script> angular.module('eventExample',[]) .controller('EventController',function($scope) { $scope.count = 0; $scope.$on('MyEvent',function() { $scope.count++; }); }]); </script> <body> <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> </body> </html>
![这里写图片描述](http://img.blog.csdn.net/20161129110045993)

Scope生命周期

浏览器接受事件的普通流程是这样的,它会执行相应的javascript回调,一旦执行完毕浏览器将会重新呈现DOM并返回多个等待事件。 当浏览器调用进入javascript的代码时,因为这些代码在Angular的执行上下文之外,意味着Angular根本无从得知model的更改。模型修改正确的执行流程是通过 $apply方法进入到Angular的执行上下文。在 $apply方法中仅仅模型改变将被Angular执行。例如一个监听DOM事件的directive,比如ng-click,它必须在 $apply方法中计算表达式的值。 在计算玩表达式的值之后, $apply会调用 $digest方法。在 $digest阶段,scope会检查所有的 $watch表达式,并且比较它们和之前的值是否相同。这种脏检查是异步的,这意味着比如 $scope.username=”angular”这项任务不会立即引起 $watch的更改,取而代之的是 $watch通知被延迟到 $digest阶段。这种延迟是可取的,因为它把多个模型的改变聚集成一个 $watch通知,并且能保证同时 $watch通知期间没有其它 $watches在运行。如果 $watches改变了模型的值,它将被强制附加到 $digest周期。 $apply()伪代码
function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}
Scope周期概要: 1.创建 在Angular程序的引导阶段,root scope通过 $injector被创建。在template链接阶段,一些directives创建新的child scopes。 2.Watcher注册 在template链接阶段,directives注册Watcher到scope。这些watches被用于将模型的值传播到DOM中。 3.Model变化 为了让变化正确的被观察到,你应该仅仅使用scope. $apply()。Angular APIs会隐含的做这些,所以不需要额外的调用 $apply()在controllers中,或者一些异步工作中比如 $http,$timeout or $interval 服务。 4.观察变化 在 $apply的末尾,Angular会在root scope内调用 $digest,会在所有的child scopes传播,在 $digest周期,所有的 $watched的表达式或者函数将被检查模型的改变,如果改变发生, watch5.Scopechildscopechildscopescope. destroy()销毁它们。这将阻止 $digest传播到改child scope,并且该child scope占用的内存也会被垃圾回收器回收。

Scopes和Directives

在编译阶段,编译器将directives和DOM template进行匹配。directives通常分为两类: 1.观察directives,比如双大括号的表达式{{expression}},用 $watch()方法注册监听器。当表达式发生改变时,这种directives需要被通知,以便可以更新视图。 2.监听directives,比如ng-click,注册一个监听器在DOM上,当DOM的监听器被激活,这个指令将执行相应的表达式,并且用$apply()方法更新视图。 当一个外部事件 (如用户操作,定时器或XHR)收到时,相关的表达式必须通过$apply()应用到scope中,以便所有的listeners被正确的更新。

创建Scopes的Directives

在大多数情况下,directives和scopes相互作用,但是不会创建一个新的scope实例。然而一些命令,比如ng-controller和ng-repeat,会创建新的child scopes并且附加child scopes到相应的DOM元素中。你可以用angular.element(aDomElement).scope()方法检索任意DOM元素对应的scope。

Controllers和Scopes

Scopes和controllers直接的相互作用有以下几种情况:
1.controllers使用Scopes来把controller的方法暴露给templates。
2.controllers定义可以改变model的方法和行为。
3.controllers可以注册watches到模型中,当controller行为被执行后watches将立刻执行

Scope $watch的性能考虑

在Angular中,脏检查scope是否有属性改变是一项非常常规的操作。因此脏检查函数必须是高效的,应该非常小心的注意到,脏检查函数不能做任何DOM访问,DOM访问比JavaScript对象属性访问慢了好几个数量级。

Scope $watch的深度

脏检查可以使用三种策略:通过引用,通过集合内容,通过值。这三种脏检查可以检测到不同的变化,并且有不同的性能特征。
1.通过引用(scope.$watch (watchExpression,listener))
检测到一个值完全变为一个新的值的变化,如果这个值是一个数组或者一个对象,它里面的值的改变是不会检测到的,这种策略是性能最高效的。

2.通过集合内容(scope.$watchCollection (watchExpression,listener))
可以检测数据和对象内部的变化:当item被添加,删除,重新排序。这种检测深度是比较浅,因为它不进入嵌套的集合。这种策略要比第一种更耗费性能,因为他需要维护集合的副本内容。然而这种策略可以最小化必要的副本。

3.通过值(Watching by value (scope.$watch (watchExpression,listener,true))
可以检测到一个任意嵌套结构的所有变化,这种检测是最强力的,但是也是最耗性能的。每一次digest它都需要完整的遍历整个数据结构,并且要保存一份完整的副本在内存中。

与浏览器的event loop集成

这张图和下面的例子讲解Angular和浏览器之间的事件循环。

1.浏览器的事件循环等待一个事件的到来。一个事件是一个用户交互,计时器事件或网络事件(从服务器响应)。
2.事件的回调被执行,然后进入JavaScript上下文,这个回调可以修改DOM结构。
3.一旦回调执行完毕,浏览器就会离开JavaScript上下文,基于DOM的变化重新渲染视图。

Angular通过提供它自己的事件进程循环修改了普通的javascript流程。它吧javascript切割成两部分:传统部分和Angular执行上下文部分。在Angular执行上下文中仅仅一些操作可以受益:Angular数据绑定,异常处理,属性检测等等。你还可以调用$apply()从javascript执行上下文进入到Angular执行上下文。请记住一点,在大多数地方(controllers,services)已经被隐式的调用了。仅仅实现当自定义事件或者调用第三方库的回调函数时,$apply()才需要被显式的调用。
1.通过调用 scope.$apply(stimulusFn)进入到Angular执行上下文。stimulusFn是你希望在Angular上下文中执行的工作。
2.Angular执行stimulusFn(),它通常修改应用程序状态。
3.Angular进入\$diges循环。这个循环由两个小的循环构成:\$evalAsync队列和 \$watch列表。\$digest循环不断迭代,直到模型稳定。这意味着\$evalAsync队列是空的,\$watch列表没有任何变化。
4.\$evalAsync队列常常用于调度当前堆栈框架之外的工作,并且该工作在浏览器视图重新渲染之前。这个通常是用setTimeout(0)来做的,但是由于setTimeout(0)的缓慢,在每次事件后,浏览器重新加载视图可能引起闪烁。
5.\$watch列表是一组在最近一次迭代可能发生变化的表达式的集合,如果变化被检测到,那么\$watch将会被调用,这个函数通常是用新的值去更新DOM。
6.一旦Angular的\$digest循环完成,将会有Angular上下文转到Javascript上下文,接下来就是浏览器去重新呈现DOM以反应所有的改变。

下面讲解一下hello world的示例,这个示例所做的是当用户输入字符到文本框中,如何实现了数据绑定。
1. 编译阶段
1.1 ng-model和input指令在<input>控件设置了一个keydown监听器。
1.2 interpolation设置一个$watch来获得name改变的通知。
2. 运行时阶段
2.1 在input控件中按下’X’键,浏览器会发出keydown事件。
2.2 input指令捕获到了input值的改变,然后调用$apply(“name = ‘X’;”)去更新在Angular上下文的model。
2.3 Angular接受 name = ‘X’; 给model。
2.4 $digest循环开始
2.5 $watch列表检测到了在name属性上的变化然后通知interpolation从而更新DOM。
2.6.Angular退出执行上下文,接着退出keydown事件,退出javascript上下文。
2.7.浏览器用新的文本去重新呈现视图。

<!DOCTYPE html> <html lang="en" ng-app> <head> <script src="script/angular.min.js"></script> </head> <body> Your name: <input type="text" ng-model="name" placeholder="World"> <hr> Hello {{name || 'World'}}! </body> </html>





如果我的文章对您有帮助,请用支付宝打赏:

angular学习五—— Scopes的更多相关文章

  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作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部