手机版

JavaScript的AngularJS框架中范围和数据绑定的详细说明

时间:2021-09-16 来源:互联网 编辑:宝哥软件园 浏览:

AngularJS简介AngularJS是谷歌首创的开源前端MVC脚本框架,既适用于普通WEB应用,也适用于SPA(单页应用,所有用户操作都在一页内完成)。与同样是MVC框架的Dojo不同,AngularJS的功能更轻。与jQuery相比,AngularJS为您节省了大量的机械绑定工作。对于一些开发速度要求高、不需要太多功能模块的非企业级WEB应用来说,AngularJS是一个非常不错的选择。AngularJS最复杂和强大的部分是它的数据绑定机制,它帮助我们更好地专注于数据建模和传输,而不是在底层DOM上执行低级操作。

在传统的基于jQuery的AngularJS作用域的WEB应用中,为了监控用户的输入等行为,需要为每个DOM元素设置一个监控方法,即监控DOM上发生的各种事件,然后jQuery会进行响应并在页面上显示出来。这种方法简单直观,但是一旦WEB应用变得庞大复杂,监控代码就会变得非常机械和冗余。更有甚者,如果DOM的事件监控管理不好,浏览器资源很容易泄露。针对以上问题,AngularJS用一系列指令替换了jQuery的事件绑定代码。为了组织各种指令之间的协调而不造成数据混乱,AngularJS在模型层上扩展了范围的概念,以配合控制器显示视觉层。Scope)AngularJS是一个指向应用程序模型的对象,它是表达式的执行环境。作用域具有层次结构,与对应的DOM几乎相同。范围可以监视表达式和传递事件。在HTML代码中,一旦定义了ng-app指令,就会生成一个作用域。ng-app生成的作用域是特殊的,它是一个$rootScope,是所有其他$ scopes的顶层。清单1。正在生成根范围。

head script src=' http : angular . min . js '/script/head dydata-ng-app=' app './body/html除了使用ng-app指令生成一个作用域外,其他指令如ng-controller和ng-repeat也会生成一个或多个作用域。此外,还可以通过AngularJS提供的工厂方法创建一个范围。这些作用域有自己的继承上下文,根作用域是$rootScope。在生成一个作用域之后,在编写AngularJS代码时,$scope对象表示这个作用域的数据实体。我们可以在$scope中定义各种数据类型,然后让HTML以{ {变量名}}的形式直接在HTML中访问这个变量。代码如下:清单2。简单的数据绑定。

scriptangular.module('app ',[])。控制器(' ctrl ',函数($ scope){ $ scope . btns={ IBM : ' IBM ' };});/script/head dydata-ng-app=' app ' div data-ng-controller=' ctrl ' button { { btns . IBM } }/button/div/body这是AngularJS中最简单的数据绑定方法,也是使用最广泛的数据绑定方法。继承的范围)AngularJS将在创建范围时检索上下文。如果上下文中已经存在一个作用域,新创建的作用域将通过JavaScript原型继承机制继承其父作用域的属性和方法(隔离作用域除外,下面讨论)。一些AngularJS指令将创建新的子作用域,并执行原型继承:ng-repeat、ng-include、ng-switch、ng-view、ng-controller,以及用scope: true和transclude: true创建的指令。下面的HTML定义了三个作用域,分别是ng-app指令创建的$rootScope,parentCtrl和childCtrl创建的子作用域,childCtrl生成的作用域也是parentCtrl的子作用域。清单3。范围的继承实例。

body data-ng-app=' app ' Div data-ng-controller=' parentCtrl ' input data-ng-model=' args ' Div data-ng-controller=' childCtrl ' input data-ng-model=' args '/Div/Div/body继承作用域符合JavaScript的原型继承机制,这意味着如果我们在子作用域中访问在父作用域中定义的属性,JavaScript首先在子作用域中查找该属性,然后在原型链上从父作用域中查找该属性如果没有找到,在AngularJS中,作用域原型链的顶部是$rootScope,AngularJS会搜索$rootScope,如果还是找不到,就会返回undefined。我们用示例代码来说明这个机制。首先,我们讨论原型数据类型的范围继承机制:清单4。范围继承示例-原始类型数据继承。

脚本类型=' text/JavaScript ' angular . module(' app ',[])。控制器(' parentCtrl ',['$scope ',函数($ scope){ $ scope . args=' IBM developer works ';}]) .控制器(' childCtrl ',['$scope ',函数($ scope){ }]);/script body data-ng-app=' app ' div data-ng-controller=' parent ctrl '输入数据-ng-model=' args ' div data-ng-controller=' child ctrl '输入数据-ng-model=' args'/div/div/body运行页面,我们会得到以下结果:

201634142417114.jpg  (18856)

结果很容易理解。虽然在childCtrl中没有定义具体的args属性,但是因为childCtrl的作用域继承了parentCtrl的作用域,所以AngularJS会在父作用域中找到args属性,并在输入框中进行设置。而且,如果我们更改第一个输入框中的内容,内容会同步反映在第二个输入框中:图2。更改第一个输入框中的内容后的页面运行结果。

201634142440035.jpg  (19250)

如果我们修改第二个输入框的内容,此时会发生什么?答案是第二个输入框的内容将不再与第一个输入框的内容同步。当更改第二个输入框的内容时,AngularJS将为childCtrl生成一个args原语类型属性,因为HTML代码中的模型被显式绑定在childCtrl的范围内。这样,根据AngularJS作用域继承的原型机制,childCtrl可以在自己的作用域中找到args属性,因此不再寻找parentCtrl的args属性。从那时起,绑定到两个输入框内容的属性已经是两个不同的实例,因此它们将不再同步。图3。更改第二个输入框的内容后的页面运行结果。

201634142457484.jpg  (17948)

如果我们按照如下方式修改代码,结合以上两种场景,想想会发生什么?清单5。范围继承实例-对象数据继承。

脚本类型=' text/JavaScript ' angular . module(' app ',[])。控制器(' parentCtrl ',['$scope ',函数($ scope){ $ scope . args={ };$ scope . args . content=' IBM developer works ';}]).控制器(' childCtrl ',['$scope ',函数($ scope){ }]);/script body data-ng-app=' app ' div data-ng-controller=' parentCtrl '输入数据-ng-model=' args . content ' div data-ng-controller=' childCtrl '输入数据-ng-model='args。内容'/div/div/body答案是两个输入框的内容总是同步的。根据AngularJS的原型继承机制,如果ng-model绑定了一个对象数据,那么AngularJS就不会为childCtrl创建一个args对象,自然也就不会有args.content属性。这样一来,args.content属性就永远不会存在于childCtrl作用域中,只能在父作用域中找到,也就是说两个输入框的改变实际上只是改变了parentCtrl作用域中的args.content属性。因此,两者的内容总是同步的。让我们看另一个例子。这次请你自己分析结果。清单6。范围继承实例-不再访问父范围的数据对象。

脚本类型=' text/JavaScript ' angular . module(' app ',[])。控制器(' parentCtrl ',['$scope ',函数($ scope){ $ scope . args={ };$ scope . args . content=' IBM developer works ';}]).控制器(' childCtrl ',['$scope ',函数($ scope){ $ scope . args={ };$ scope . args . content=' IBM developer works ';}]);/script body data-ng-app=' app ' div data-ng-controller=' parentCtrl '输入数据-ng-model=' args . content ' div data-ng-controller=' childCtrl '输入数据-ng-m Odel=' args . content '/div/div/body答案是两个输入框的内容永远不会同步。隔离作用域隔离作用域是AngularJS中非常特殊的作用域,只出现在指令中。在方向性的定义中,我们添加了最后一个scope:{}属性,并为此方向性创建了一个隔离范围。清单7。指令创建一个独立的范围。

angular.module('isolate ',[])。指令(' isolate ',function(){ return { scope : },};})隔离作用域最大的特点是不会原型化其父作用域,与外部父作用域保持相对独立。因此,如果您想在定义了独立作用域的AngularJS指令中访问其父作用域的属性,则获得的值是未定义的。代码如下:清单8。孤立作用域的隔离。

脚本类型=' text/JavaScript ' angular . module(' app ',[])。控制器(' ctrl ',['$scope ',函数($ scope){ $ scope . args={ };}]) .指令(' isolateDirective ',function(){ return { scope : } },link : function($scope,$element,$attr) { console.log($scope。$ args);//输出未定义} };});/script body data-ng-app=' app ' div data-ng-controller=' ctrl ' div data-isolate-directive/div/body在上面的代码中,通过在directive中声明scope属性来创建一个作用域,其父作用域就是ctrl所属的作用域。但是,此作用域是独立的,因此它不能访问父作用域中的任何属性。这种设计机制的优点是可以创建一些列可重用的指令,这些指令不会在拥有的属性值上相互串扰,也不会产生任何副作用。AngularJS隔离作用域的数据绑定在继承的作用域中,我们可以选择子作用域直接操作父作用域的数据,实现父作用域与子作用域的通信。在隔离范围内,子范围不能直接访问和修改父范围的属性和值。为了使隔离范围能够与外界通信,AngularJS提供了三种方法来打破隔离范围的限制。单向绑定(@或@attr)是AngularJS的隔离范围和外部父范围之间最简单的数据通信。绑定对象只能是父作用域中的字符串值,并且是单向只读引用,因此不能修改父作用域中的字符串值。此外,该字符串必须在父范围的HTML节点中以attr(属性)的形式声明。使用此绑定方法时,需要在指令的scope属性中明确指定引用父作用域中HTML字符串的属性,否则会引发异常。代码如下:清单9。单向绑定示例。

编写angular.module脚本(' isolateScope ',[])。指令(' isolateDirective ',function(){ return { replace : true,template : ' button { { isolates } }/button ',scope : { isolates : '@ ',},link : function($scope,$element,$ attr){ $ scope . isolates=' developer works ';} };}) .控制器(' ctrl ',函数($ scope){ $ scope . btns=' IBM ';});/script body data-ng-app=' isolateScope ' Div data-ng-controller=' ctrl ' button { { btns } }/button Div data-isolate-directional data-isolates=' { { btns } } '/Div/Div/body简单地分析了上述代码,通过在指令中声明scope: { isolates: ' @ ' } @ ' },指令在父作用域中具有HTML属性data-isolates的值,该值在controller ctrl中被赋值为' IBM '。因此,代码运行的结果是页面上有两个名为IBM的按钮。我们还注意到,隔离在link函数中被修改了,但最终不会反映在运行结果中。这是因为隔离总是绑定到父作用域中的btns字符串。如果父作用域中的BTN没有改变,无论您如何修改隔离作用域中的隔离,它都不会起作用。引用绑定(或attr)通过这种形式的绑定,被隔离的作用域将有能力访问父作用域中的函数对象,从而可以执行父作用域中的函数来获得一些结果。这种方式的绑定,和单向绑定一样,只能以只读方式访问父函数,这个函数的定义必须写在父作用域HTML中的attr节点上。虽然这种方式的绑定不能修改父作用域attr设置的函数对象,但是可以通过执行函数来改变父作用域中某些属性的值,达到一些预期的效果。代码如下:清单10。参考绑定示例。

编写angular.module脚本(' isolateScope ',[])。指令(' isolateDirective ',function(){ return { replace : true,scope : { isolates : ' ',},link : function($scope,$element,$ attr){ var func=$ scope . isolates();func();} };}) .controller('ctrl ',function($ scope){ $ scope . func=function(){ console . log(' IBM developer works ');} });/script body data-ng-app=' isolate scope ' div data-ng-controller=' ctrl ' div data-isolate-directional data-isolates=' func '/div/div/body在这个例子中,浏览器的控制台会输出一段“IBM DeveloperWorks”。在上面的代码中,我们在父作用域中指定了一个函数对象$scope.func,并通过绑定HTML属性在隔离作用域中引用了func。应注意func对象在link函数中的使用。$ scope . isolation只得到函数对象,不调用这个对象,所以我们需要在调用$ scope . isolation后调用这个函数,才能得到真正的执行结果。双向绑定(=or=attr)双向绑定赋予AngularJS隔离范围与外界最自由的双向数据通信功能。在双向绑定模式下,隔离范围可以直接读写父范围中的属性和数据。与上面两个独立作用域定义的数据绑定一样,双向绑定必须通过在父作用域的HTML中设置属性节点来绑定。双向绑定非常适合子指令需要频繁与父作用域交互且数据复杂的一些场景。但是,由于父作用域中的属性和对象可以自由读写,因此在多个指令共享父作用域数据的一些场景中需要谨慎使用,这很容易造成数据混乱。代码如下:清单11。双向绑定示例。

编写angular.module脚本(' isolateScope ',[])。指令(' isolateDirective ',function(){ return { replace : true,template : ' button { { isolates } }/button ',scope : { isolates : '=',},link : function($scope,$element,$ attr){ $ scope . isolates . IBM=' IBM ';} };}) .控制器(' ctrl ',函数($ scope){ $ scope . btns={ IBM : ' IBM ',dw : ' developer works ' };});/script body data-ng-app=' isolateScope ' div data-ng-controller=' ctrl ' Button { { btns . dw } }/Button Button { { btns . ibm } }/Button data-isolate-directive data-isolates=' btns '/div/div/body上面代码的结果是浏览器页面上有三个按钮,第一个按钮的标题为“DeveloperWorks”,第二个和第三个按钮的标题为“IBM”。最初,父作用域中的$scope.btns.IBM是小写的“IBM”。通过双向绑定,将父作用域中的ibm重写为隔离作用域中的大写“ibm”并直接生效,同时改变父作用域的值。

总结:AngularJS框架因其重量轻、MVC特性清晰等特点,推出后非常受欢迎,在实践中也很容易上手。AngularJS难以掌握和理解的是它的范围和绑定机制。本文着重对范围和绑定机制进行分析和探讨,希望读者能熟练理解和掌握这一内容。

版权声明:JavaScript的AngularJS框架中范围和数据绑定的详细说明是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。