了解角度数据双向绑定
AngularJS是一个优秀的前端JS框架,已经在很多Google产品中使用。AngularJS具有许多特点,其核心是MVVM、模块化、自动双向数据绑定、语义标注、依赖注入等。
1.什么是数据的双向绑定Angular实现了双向绑定机制。所谓双向绑定,就是界面的操作可以实时反映到数据上,数据的变化可以实时显示到界面上。
一个最简单的例子是:
div ng-controller=' countertrl ' span ng-bind=' counter '/span button ng-click=' counter '递增/button/div function countertrl($ scope){ $ scope . counter=1;}这个例子很简单。每次点击一个按钮,界面上的数字就会增加一。
2.数据双向绑定原则1。深刻理解实现用户控制手机列表显示顺序的特点。动态排序可以通过添加一个新的模型属性,将其与迭代器集成,然后让数据绑定完成剩下的工作来实现。
模板(app/index.html)
search : input ng-model=' query ' sort by : Select ng-model=' order prop '选项值='name '按字母顺序排列/选项值=' age ' latest/option/Select ul class=' phones ' Li ng-repeat=' phones in phones | filter : query | order by : order prop ' { phone . name } } p { { phone . snippet } }/p/Li/ul在index.html中进行了以下更改:
首先,添加了一个名为orderProp的select标记,这样用户就可以选择提供的两种排序方法。
然后,在过滤器过滤器之后添加orderBy过滤器,以处理进入迭代器的数据。OrderBy过滤器将一个数组作为输入,复制一个副本,然后对副本进行重新排序,并将它们输出到迭代器。AngularJS在select元素和orderProp模型之间创建双向绑定。然后,OrderProp被用作orderBy筛选器的输入。
当数据模型改变时(例如,用户在下拉菜单中选择不同的顺序),AngularJS的数据绑定将自动更新视图。没有笨拙的DOM操作。
控制器(app/js/controller . js)
function phone listcrl($ scope){ $ scope . phones=[{'name': ' Nexus S ',' snippet': 'Fast刚用Nexus S . ',' age ' : 0 0 },{ ' name ' : '带Wi-Fi的摩托罗拉XOOM ',' snippet': '下一代,下一代平板电脑',' age': 1},{'name': 'MOTOROLA XOOM ',' snippet': '下一代平板电脑',' age ' : 2 }];$ scope.orderProp=' age}修改了型号为——的电话的数组——,并为每个电话记录添加了年龄属性。根据年龄属性。在控制器代码中添加了一行,使orderProp的默认值为age。如果我们不设置默认值,模型将保持未初始化状态,直到用户在下拉菜单中选择一个序列。
现在是我们讨论双向数据绑定的时候了。请注意,当应用程序加载到浏览器中时,下拉菜单中会选择“新建”。这是因为我们在控制器中将orderProp设置为“age”。因此,绑定的工作方向是从我们的模型到用户界面——,也就是说,将数据从模型绑定到视图。现在从下拉菜单中选择“按字母顺序”,数据模型会同时更新,手机列表数组也会重新排序。此时,数据绑定从另一个方向——生效,即从视图到模型的数据绑定。
2.原理分析下面的原理和思路其实是非常基础的,可以认为是一个三步走的计划:
我们需要一种将UI元素和属性相互绑定的方法。我们需要监控属性和UI元素的变化。我们需要让所有绑定的对象和元素意识到变化。实现上述想法的方法还有很多。一个简单有效的方法是使用PubSub模式。想法很简单:我们使用数据特征绑定HTML代码,所有绑定的JavaScript对象和DOM元素都会订阅一个PubSub对象。只要JavaScript对象或者一个HTML输入元素监控到数据的变化,就会触发绑定到PubSub对象的事件,这样其他绑定的对象和元素就会做出相应的变化。
3.发布者-订阅者模式(PubSub Pattern)这种模式背后的主要驱动力是促进松散耦合的形成。在这种模式下,不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动,并在状态更改后得到通知。订阅者也称为观察者,而用于补充观察的对象称为发布者或主题。当一个重要的事件发生时,发布者将通知(调用)所有的订阅者,并且可能经常以事件对象的形式传递消息。
假设有一个出版商,报纸,每天出版报纸和月刊。订户乔将随时被告知这一消息。
这个paper对象需要一个subscribers属性,它是一个存储所有订阅者的数组。订阅行为只是将其添加到该数组中。当一个事件发生时,paper将遍历订户列表并通知他们。意味着通知调用订阅方对象的方法。因此,当用户订阅信息时,订阅者需要向paper的subscribe()提供其方法之一。
Paper还提供了unsubscribe()方法,这意味着要从订阅者数组中删除订阅者(即订阅者属性)。论文的最后一个重要方法是publish(),它调用这些订阅者的方法。总而言之,publisher对象纸张需要有以下成员:
订阅者数组subscribe()将订阅者添加到订阅者数组unsubscribe()从订阅者数组中删除订阅者publish()循环遍历订阅者数组中的每个元素,并调用它们在注册时提供的方法。这三种方法都需要一个类型参数,因为发布者可能会触发多个事件(比如同时发布一份杂志和一份报纸),用户可能只选择订阅其中一个,而不订阅另一个。
由于这些成员对任何发布者对象都是通用的,因此将它们实现为独立对象的一部分是有意义的。这样,我们可以将它复制到任何对象中,并将任何给定的对象变成发布者。
3.用Jquery简单实现DOM事件的订阅和发布。用jQuery实现非常简单。接下来,我们将使用jQuery,如下所示:
函数DataBinder(object_id ) { //使用一个jQuery对象作为简单的PubSub var PubSub=jQuery({ });//我们期望在form : data-bind-object _ id=' property _ name ' var data _ attr=' bind-' object _ id,message=object_id ':change '中有一个指定绑定//的' data '元素;//监听具有数据绑定属性的元素上的更改事件,并将它们代理//到PubSub,以便将更改“广播”到所有连接的对象jQuery(文档)。on('change ','[data-' data_attr ']',function(evt){ var $ input=jQuery(this);pubSub.trigger(message,[ $input.data(data_attr),$ input . val()]);});//PubSub将更改传播到所有绑定元素,将//输入标记的值或其他标记的HTML内容设置为pubSub.on(message,function(evt,prop_name,new _ val){ jQuery('[data-' data _ attr '=' prop _ name ']')。每个(function(){ var $ bound=jQuery(this);if ($bound.is('input,textarea,select '){ $ bound . val(new _ val);} else { $ bound . html(new _ val);} });});返回pubSub}对于上述实现,以下是用户模型最简单的实现方法:
函数User(uid){ var binder=new DataBinder(uid),user={ attributes: {,//属性设置器使用DataBinder PubSub set:函数(attr_name,val){ this . attributes[attr _ name]=val发布更改;binder.trigger(uid ':change ',[ attr_name,val,this]);},get:函数(attr_name ) {返回this . attributes[attr _ name];},_ binder : binder };//订阅PubSub binder . on(uid ' : change ',函数(evt,attr_name,new_val,initiator ) { if (initiator!==user ) { user.set(attr_name,new _ val);} });返回用户;}现在,如果我们想要将User模型的属性绑定到UI,我们只需要将适当的数据特征绑定到相应的HTML元素。
//javascriptvar用户=新用户(123);user.set('name ',' Wolfgang ');//html输入类型=' number ' data-bind-123=' name '/以便输入值自动映射到用户对象的name属性,反之亦然。至此,这个简单的实现就完成了。
四个。Angular实现双向数据绑定。Angular主要通过范围实现双向数据绑定。AngularJS的范围包括以下四个主要部分:
摘要循环和脏检查,包括监视、摘要和$apply。范围继承——这种机制使我们能够创建范围继承来共享数据和事件。数组和对象集合的有效脏检查。事件系统-开启、发出和$广播。
我们主要解释第一个Angular数据绑定是如何实现的。
1 .摘要循环和脏检查,包括watch、digest和$apply Browser事件循环和Angular.js扩展我们的浏览器一直在等待事件,比如用户交互。如果你点击一个按钮或者在输入框输入一些东西,事件的回调函数就会在javascript解释器中执行,然后你就可以做任何DOM操作了。当回调函数被执行时,浏览器将相应地改变DOM。Angular扩展了这个事件周期,并生成了一个执行环境,有时称为angular context(这是一个重要的概念)。
(2)观察列表每次将某物绑定到UI时,都会在$watch队列中插入一个$watch。想象一下,$watch是可以检测到它所监控的模型变化的东西。
当我们的模板被加载时,也就是在链接阶段(Angular分为编译阶段和链接阶段),Angular解释器会查找每个指令,然后生成每个需要的$watch。
$digest Loop还记得我前面提到的扩展事件循环吗?当浏览器接收到可以由角度上下文处理的事件时,将触发摘要循环。这个循环是两个较小循环的组合。一个处理evalAsync队列,另一个处理观察队列。这和什么有关?Digest将遍历我们的手表,然后询问它的属性和值是否有任何变化,这已经在$watch的队列中检查过了。
这叫做脏检查。既然$手表都检查过了,那就有必要问一下:你更新过$手表了吗?如果至少有一个已经更新,这个循环将再次被触发,直到所有的$ watches都没有改变。这样就可以保证每一款车型都不会再有变化。请记住,如果循环超过10次,它将引发异常以防止无限循环。当$digest循环结束时,DOM会相应地改变。
示例:controllers.js
app.controller('MainCtrl ',function(){ $ scope . name=' Foo ';$ scope . changefooo=function(){ $ scope . name=' Bar ';}});{ { name } } button ng-click=' change foo()'更改名称/button在这里,我们有一个$watch,因为ng-click不会生成$watch(该函数不会更改)。
当我们按下按钮时,浏览器接收到一个事件并进入角度上下文(原因将在后面解释)。$digest循环开始执行,并查询每个$watch是否已更改。由于$watch monitoring $scope.name报告了该更改,它将强制执行另一个$digest循环。在的新$digest循环中未检测到任何更改。浏览器收回控制权,更新与$ scope.name的新值对应的DOM,这里非常重要(对很多人来说非常痛苦)的是,每一个进入angular上下文的事件都会执行一个$digest循环,也就是说每一次我们进入一个字母循环,都会检查整个页面上所有的$ watches。
(4)通过$apply输入角度上下文。谁来决定哪些事件进入了角度语境,哪些没有?$apply!
如果在事件被触发时调用apply,它将进入angularcontext,但如果不调用,它将不会进入。现在你可能会问:我刚才例子里没有叫apply,为什么?Angular为你做的!因此,当您用ng-click单击一个元素时,时间被封装到一个apply调用中。如果您有一个带有ng model=' foo '的输入框,然后您遇到一个f,事件将像这样调用apply(' foo=' f ';'。)。
Angular什么时候不会自动申请我们?这是棱角分明的新手常见的痛苦。为什么我的jQuery不更新我绑定的内容?因为jQuery没有调用apply,所以事件没有进入angular上下文,并且$digest循环从未执行过。
2.具体实现AngularJS的作用域是通用的JavaScript对象,在其上可以绑定自己喜欢的属性和其他对象。然而,他们也添加了一些功能来观察数据结构的变化。这些观察函数都是通过脏检查实现的,并且都是在一个摘要循环中执行的。scope对象创建一个test/scope_spec.js文件,并向其中添加以下测试代码:
test/scope _ spec . js-/* js hint global strict : true */* global scope : false */“使用strict”;description(' Scope '),function() {it('可以构造并用作对象',function(){ var Scope=new Scope();scope . aproperty=1;expect(scope.aProperty)。托比(1);});});该测试用于创建一个范围,并为其分配任意值。我们可以轻松通过这个测试:创建src/scope.js文件,并向其中添加以下内容:src/scope.js。
-/* js hint global strict : true */'使用strict ';函数范围(){}在这个测试中,我们为这个范围分配了一个属性。这就是范围属性的工作方式。它们是普通的JavaScript属性,没有什么特别的。在这里,你根本不需要调用特殊的setter,也不需要限制你做的赋值类型。真正的神奇在于两个特殊的功能:观赏和消化。现在我们来看看这两个函数。
监控对象属性:watch和digestwatch和digest是同一枚硬币的两面。它们都构成了$digest周期的核心:应对数据变化。
为了实现这个功能,我们首先定义一个测试文件,断言可以使用watch注册一个监视器,当有人调用digest时,就会调用监视器的监听功能。
在scope_spec.js文件中添加嵌套的描述块。并创建一个beforeEach函数来初始化这个作用域,这样我们就可以在每次测试中重复它:
测试/范围_spec.js
-description(' Scope '),function() {it('可以构造并用作对象',function(){ var Scope=new Scope();scope . aproperty=1;expect(scope.aProperty)。托比(1);});description(' digest ',function(){ var scope;before EACH(function(){ Scope=new Scope();});它('在first $digest上调用watch的侦听器函数',function(){ var watch fn=function(){ return ' wat ';};var listener fn=jasmine . createspy();范围。$watch(watchFn,listener fn);范围。$ digest();期望(listenerFn)。tohavebeenabled();});});});在上面的测试中,我们调用watch在这个范围上注册一个监视器。现在我们对监控函数本身不感兴趣,所以我们提供了一个返回常量值的函数。作为监听功能,我们提供了一个JasmineSpy。然后我们调用digest并检查侦听器是否被真正调用。
首先,这个范围需要一些地方来存储所有注册的监视器。现在我们向作用域构造函数添加一个数组来存储它们:
src/scope.js
-函数Scope(){ this。$ $ watchers=[];}上述代码中的$ $前缀在AngularJS框架中被认为是私有变量,不应该在应用程序外部调用。现在我们可以定义手表功能了。它采用两个函数作为参数,并将它们存储在$watchers数组中。我们希望每个范围对象都有这个功能,所以我们将其添加到范围的原型中:
src/scope . js-scope . prototype . $ watch=function(watch fn,listener fn){ var watcher={ watch fn : watch fn,listener fn : listener fn };这个。$ $ watchers . unshift(watchers);};最后,我们应该有一个消化功能。现在,让我们定义一个摘要函数的简化版本,它只迭代所有注册的监视器并调用它们的监听函数:摘要可以连续迭代所有监视函数,直到被监视的值停止变化。一定要消化几次,这样我们就可以得到应用于显示器并依赖于其他显示器的更改。
首先,我们创建一个名为$$digestOnce的新名称,并对其进行调整,使其可以在所有监视器上运行,然后返回一个布尔值来显示是否有任何更改:src/scope.js
-scope . prototype . $ $ digest once=function(){ var length=this。$ $ watchers.length风险值观察器、新值、旧值、脏值;while(length - ){ watcher=this。$ $ watchers[length];new value=watcher . watch fn(this);oldValue=watcher.lastif(newValue!==old value){ watcher . last==new value;watcher.listenerFn(newValue,oldValue,this);dirty=true} }返回脏;};接下来,我们重新定义digest,这样它就可以运行一个“外循环”,并在发生变化时调用$digestOnce:
src/scope.js
-scope . prototype . $ digest=function(){ var dirty;做{ dirty=这个。$ $ digestOnce();} while(脏);};以上是Angular数据双向绑定的相关介绍,希望对大家的学习有所帮助。
版权声明:了解角度数据双向绑定是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。