配色: 字号:
AngularJS技术参考手册
2020-09-15 | 阅:  转:  |  分享 
  
......用户ID:{{cu
rrentUser.userId}}
用户名:{{currentUser.userName}}
用户简介:{{cu
rrentUser.userInfo}}
4.8依赖注入DI通过依赖注入,ng想要推崇一种声明式的开发方式,
即当我们需要使用某一模块或服务时,不需要关心此模块内部如何实现,只需声明一下就可以使用了。在多处使用只需进行多次声明,大大提高可复
用性。比如我们的controller,在定义的时候用到一个$scope参数。app.controller(''testC'',fun
ction($scope){});如果我们在此处还需操作其他的东西,比如与浏览器地址栏进行交互。我们只需再多添一个参数$loca
tion进去:app.controller(''testC'',function($scope,$location){});这样便可
以通过$location来与地址栏进行交互了,我们仅仅是声明了一下,所需的其他代码,框架已经帮我们注入了。我们很明显的感觉到了这个
函数已经不是常规意义上的javascript函数了,在常规的函数中,把形参换一个名字照样可以运行,但在此处若是把$scope换成别
的名字,程序便不能运行了。因为这是已经定义好的服务名称。这便是依赖注入机制。顺理成章的推断,我们可以自己定义模块和服务,然后在需要
的地方进行声明,由框架来替我们注入。来看下我们如何定义一个服务:app.factory(''tpls'',function(){
return[''tpl1'',''tpl2'',''tpl3'',''tpl4''];});看上去相当简单,是因为我在这里仅仅是直接返
回一个数组。在实际应用中,这里应该是需要向服务器发起一个请求,来获取到这些模板们。服务的定义方式有好几种,包括使用provider
方法、使用factory方法,使用service方法。它们之间的区别暂且不关心。我们现在只要能创建一个服务出来就可以了。我使用了f
actory方法。一个需要注意的地方是,框架提供的服务名字都是由$开头的,所以我们自己定义的最好不要用$开头,防止发生命名冲突。定
义好一个服务后,我们就可以在控制器中声明使用了,如下:app.controller(''testC'',function($sco
pe,tpls){$scope.question=questionModel;$scope.nowTime=new
Date().valueOf();$scope.templates=tpls;//赋值到$scope中$scope.a
ddOption=function(){varo={content:''''};$scope.question
.options.push(o);};$scope.delOption=function(index){$scope
.question.options.splice(index,1);};});此时,若在模板中书写如下代码,我们便可以获取到服
务tpls所提供的数据了:模板:plates">{{t}}  
4.9路由(route)在谈路由机制前有必要先提一下现在比
较流行的单页面应用,就是所谓的singlepageAPP。为了实现无刷新的视图切换,我们通常会用ajax请求从后台取数据,然后
套上HTML模板渲染在页面上,然而ajax的一个致命缺点就是导致浏览器后退按钮失效,尽管我们可以在页面上放一个大大的返回按钮,让用
户点击返回来导航,但总是无法避免用户习惯性的点后退。解决此问题的一个方法是使用hash,监听hashchange事件来进行视图切换
,另一个方法是用HTML5的historyAPI,通过pushState()记录操作历史,监听popstate事件来进行视图切换
,也有人把这叫pjax技术。基本流程如下:如此一来,便形成了通过地址栏进行导航的深度链接(deeplinking),也就是我们所
需要的路由机制。通过路由机制,一个单页应用的各个视图就可以很好的组织起来了。4.9.1ngRoute内容ng的路由机制是靠ng
Route提供的,通过hash和history两种方式实现了路由,可以检测浏览器是否支持history来灵活调用相应的方式。ng的
路由(ngRoute)是一个单独的模块,包含以下内容:服务$routeProvider用来定义一个路由表,即地址栏与视图模板的映射
服务$routeParams保存了地址栏中的参数,例如{id:1,name:''tom''}服务$route完成路由匹配,并
且提供路由相关的属性访问及事件,如访问当前路由对应的controller指令ngView用来在主视图中指定加载子视图的区域以上内
容再加上$location服务,我们就可以实现一个单页面应用了。下面来看一下具体如何使用这些内容。4.9.2ng的路由机制第一
步:引入文件和依赖ngRoute模块包含在一个单独的文件中,所以第一步需要在页面上引入这个文件,如下:http://code.angularjs.org/1.2.8/angular.min.js">src="http://code.angularjs.org/1.2.8/angular-route.min.js">pt>光引入还不够,我们还需在模块声明中注入对ngRoute的依赖,如下:varapp=angular.module(''M
yApp'',[''ngRoute'']);完成了这些,我们就可以在模板或是controller中使用上面的服务和指令了。下面我们需
要定义一个路由表。第二步:定义路由表$routeProvider提供了定义路由表的服务,它有两个核心方法,when(path,
route)和otherwise(params),先看一下核心中的核心when(path,route)方法。when(path,
route)方法接收两个参数,path是一个string类型,表示该条路由规则所匹配的路径,它将与地址栏的内容($location
.path)值进行匹配。如果需要匹配参数,可以在path中使用冒号加名称的方式,如:path为/show/:name,如果地址栏是
/show/tom,那么参数name和所对应的值tom便会被保存在$routeParams中,像这样:{name:tom}。我
们也可以用进行模糊匹配,如:/show/:name将匹配/showInfo/tom。route参数是一个object,用来指
定当path匹配后所需的一系列配置项,包括以下内容:controller//function或string类型。在当前模板上执行
的controller函数,生成新的scope;controllerAs//string类型,为controller指定别名;t
emplate//string或function类型,视图z所用的模板,这部分内容将被ngView引用;templateUrl
//string或function类型,当视图模板为单独的html文件或是使用了plate">定义模板时使用;resolve//指定当前controller所依赖的其他模块;redirectTo//重定向的
地址。最简单情况,我们定义一个html文件为模板,并初始化一个指定的controller:functionemailRouteC
onfig($routeProvider){$routeProvider.when(''/show'',{controller:
ShowController,templateUrl:''show.html''}).when(''/put/:name'',{
controller:PutController,templateUrl:''put.html''});};other
wise(params)方法对应路径匹配不到时的情况,这时候我们可以配置一个redirectTo参数,让它重定向到404页面或者是
首页。第三步:在主视图模板中指定加载子视图的位置我们的单页面程序都是局部刷新的,那这个“局部”是哪里呢,这就轮到ngView出
马了,只需在模板中简单的使用此指令,在哪里用,哪里就是“局部”。例如:或:>我们的子视图将会在此处被引入进来。完成这三步后,你的程序的路由就配置好了。4.9.3路由示例下面我们将用
一个例子(例09)来说明路由的使用方式及步骤:1.为demoApp添加一个路由,代码如下:demoApp.config([''$ro
uteProvider'',function($routeProvider){$routeProvider.when(''/li
st'',{templateUrl:''route/list.html'',controller:''routeListC
ontroller''}).when(''/list/:id'',{templateUrl:''route/detail.htm
l'',controller:''routeDetailController''}).otherwise({redirect
To:''/list''});}]);/list对应为:route/list.html页面,显示用户列表;/list/:
id对应于route/detail.html页面,显示用户详细信息。2.为list.html和detail.html分别声明Con
troller:routeListController和routeDetailController。demoApp.control
ler(''routeListController'',function($scope){$scope.users=[{us
erId:"zhangsan",userName:"张三",userInfo:"我是张三,我为自己带盐!"},{userId:"l
isi",userName:"李四",userInfo:"我是李四,我为卿狂!"},{userId:"woshishui",use
rName:"我是谁",userInfo:"我是谁!我是谁!我是谁!"}];});demoApp.controller(''ro
uteDetailController'',function($scope,$routeParams,userService)
{$scope.userDetail=userService.getUser($routeParams.id);});r
outeDetailController中如上面提到的一样,注入了userService服务,在这里直接拿来用。3.创建list.
html和detail.html页面,代码如下:

Route:List.html(用户列表页面)


    Id}}">{{user.userName}}

Route:detai
l.html(用户详细信息页面)

用户名:{{userDet
ail.userName}}

用户ID:{{userDetail.userId}}span>用户名:{{userDetail.userName}}
用户简介:n>{{userDetail.userInfo}}
返回>
4.路由局部刷新位置:

AngularJS路由(Route)示例

<
/div>4.10NG动画效果4.10.1NG动画效果简介NG动画效果,现在可以通过CSS3或者是JS来实现,如果是通过JS来
实现的话,需要其他JS库(比如JQuery)来支持,实际上底层实现还是靠其他JS库,只是NG将其封装了,使其更易使用。NG动画效果
包含以下几种:enter:元素添加到DOM中时执行动画;leave:元素从DOM删除时执行动画;move:移动元素时执行动画;be
foreAddClass:在给元素添加CLASS之前执行动画;addClass:在给元素添加CLASS时执行动画;beforeRe
moveClass:在给元素删除CLASS之前执行动画;removeClass:在给元素删除CLASS时执行动画。其相关参数为:v
arngModule=angular.module(''YourApp'',[''ngAnimate'']);demoApp.
animation(''.my-crazy-animation'',function(){return{enter:fun
ction(element,done){//runtheanimationhereandcalldonewhe
ntheanimationiscompletereturnfunction(cancelled){//this
(optional)functionwillbecalledwhentheanimation//complet
esorwhentheanimationiscancelled(thecancelled//flagwill
besettotrueifcancelled).};},leave:function(element,
done){},move:function(element,done){},//animationthat
canbetriggeredbeforetheclassisaddedbeforeAddClass:funct
ion(element,className,done){},//animationthatcanbetrigg
eredaftertheclassisaddedaddClass:function(element,classN
ame,done){},//animationthatcanbetriggeredbeforethecla
ssisremovedbeforeRemoveClass:function(element,className,do
ne){},//animationthatcanbetriggeredaftertheclassisre
movedremoveClass:function(element,className,done){}};}
);4.10.2动画效果示例下面我们来看下DEMO中的例子(例10)。1.首先,我们在demoApp下定义一个动画效果,匹配CL
ASS:”.border-animation”/定义动画/demoApp.animation(''.border-animat
ion'',function(){return{beforeAddClass:function(element,cla
ssName,done){$(element).stop().animate({''border-width'':1},2000
,function(){done();});},removeClass:function(element,clas
sName,done){$(element).stop().animate({''border-width'':50},300
0,function(){done();});}};});动画效果的含义就是:在匹配CLASS为border-anima
tion的元素添加一个CLASS之前使其边框的宽度在2秒内变为1PX;并在其移除一个CLASS时使其边框的宽度在3秒内变为50PX
。2.视图中的代码如下(主要,其他相关样式请查看例子代码):how="testShow">Show=!testShow">Changeng-show为false时会为其加上“ng-hide“的CLASS;ng
-show为true时会为其移除“ng-hide“的CLASS,从而触发动画效果。3.其他代码:demoApp.controlle
r("test10Controller",function($scope,$animate){$scope.testShow
=true;});还有一个例子,动画是指HTML元素的转换为您提供运动的幻觉。选中复选框以隐藏DIV:"ngAnimate">HidetheDIV:ck">运行效果:应用程序不应充满动画,但某些动画可以使
应用程序更容易理解。5功能演示5.1数据绑定下面我们将编写一个表单,用来计算一个订单在不同币种下的总价。首先,我们做一个单币种
表单,它有数量和单价两个输入框,并且把数量和单价相乘得出该订单的总价。etacharset="utf-8"/>订单价格计算.min.js">">订单:
数量:y">
单价:>
总价:{{qtycost|currency}}

5.2控制器现在,我们添加一些逻辑,以便让这个例子支持不同的币种。它将允许我们使用不同的币种来输入、
计算和支付这个订单。5.2.1index.html内容">订单价格计算rc="angular.min.js">
llerasinvoice">订单:
数量:0"ng-model="invoice.qty"required>
单价:"number"min="0"ng-model="invoice.cost"required>el="invoice.inCurr">
{{c}}
总价:"cininvoice.currencies">{{invoice.total(c)|currency:c}}an>
支付
5.2.2invoice1.js内容angular.module(''in
voice1'',[]).controller(''InvoiceController'',function(){this.
qty=1;this.cost=2;this.inCurr=''EUR'';this.currencies=[''
USD'',''EUR'',''CNY''];this.usdToForeignRates={USD:1,EUR:0.74
,CNY:6.09};this.total=functiontotal(outCurr){returnthis
.convertCurrency(this.qtythis.cost,this.inCurr,outCurr);};
this.convertCurrency=functionconvertCurrency(amount,inCurr,o
utCurr){returnamountthis.usdToForeignRates[outCurr]1/t
his.usdToForeignRates[inCurr];};this.pay=functionpay(){win
dow.alert("谢谢!");};});5.3服务(Service)现在,InvoiceController包含了我们这
个例子中的所有逻辑。如果这个应用程序的规模继续成长,最好的做法是:把控制器中与视图无关的逻辑都移到"服务(service)"中。
以便这个应用程序的其他部分也能复用这些逻辑。接下来,就让我们重构我们的例子,并且把币种兑换的逻辑移入到一个独立的服务(servic
e)中。5.3.1index.html内容d>订单价格计算gular.min.js">ptsrc="invoice2.js">"ng-controller="InvoiceControllerasinvoice">订单:

数量:
单价:cost"required>eat="cininvoice.currencies">{{c}}
v>总价:nvoice.currencies">{{invoice.total(c)|currency:c}}ttonclass="btn"ng-click="invoice.pay()">支付v>5.3.2finance2.js内容angular.module(''finance2'',[])
.factory(''currencyConverter'',function(){varcurrencies=[''U
SD'',''EUR'',''CNY''],usdToForeignRates={USD:1,EUR:0.74,CNY:
6.09};return{currencies:currencies,convert:convert};fun
ctionconvert(amount,inCurr,outCurr){returnamountusdToFor
eignRates[outCurr]1/usdToForeignRates[inCurr];}});5.3.3in
voice2.js内容angular.module(''invoice2'',[''finance2'']).controller(''
InvoiceController'',[''currencyConverter'',function(currencyConve
rter){this.qty=1;this.cost=2;this.inCurr=''EUR'';this.cu
rrencies=currencyConverter.currencies;this.total=functionto
tal(outCurr){returncurrencyConverter.convert(this.qtythis.c
ost,this.inCurr,outCurr);};this.pay=functionpay(){window
.alert("谢谢!");};}]);5.4调用WebAPIhttps://www.angularjs.net.cn/t
utorial/18.htmlhttps://www.angularjs.net.cn/tutorial/18.html6Ang
ularJS进阶6.1数据绑定原理研究Angular用户都想知道数据绑定是怎么实现的。你可能会看到各种各样的词汇:$watch、$
apply、$digest、dirty-checking...它们是什么?它们是如何工作的呢?这里我想回答这些问题,其实它们在官方
的文档里都已经回答了,但是我还是想把它们结合在一起来讲,但是我只是用一种简单的方法来讲解,如果要想了解技术细节,查看源代码。6.1
.1AngularJS扩展事件循环我们的浏览器一直在等待事件,比如用户交互。假如你点击一个按钮或者在输入框里输入东西,事件的回调
函数就会在javascript解释器里执行,然后你就可以做任何DOM操作,等回调函数执行完毕时,浏览器就会相应地对DOM做出变化。
(记住,这是个重要的概念),为了解释什么是context以及它如何工作,我们还需要解释更多的概念。6.1.2$watch队列每
次你绑定一些东西到你的DOM上时你就会往$watch队列里插入一条$watch。想象一下$watch就是那个可以检测它监视的mod
el里时候有变化的东西。例如你有如下的代码:/Viewindex.html/User:t"ng-model="user"/>Password:ass"/>在这里我们有个$scope.user,他被绑定在了第一个输入框上,还有个$scope.pass,它被绑定在了第二个输
入框上,然后我们在$watchlist里面加入两个$watch。再看下面的例子:/Controllercontroller
s.js/app.controller(''MainCtrl'',function($scope){$scope.foo=
"Foo";$scope.world="World";});/Viewindex.html/Hello,{{
World}}这里,即便我们在$scope上添加了两个东西,但是只有一个绑定在了DOM上,因此在这里只生成了一个$watch。再
看下面的例子:/Controllercontrollers.js/app.controller(''MainCtrl'',
function($scope){$scope.people=[...];});/Viewindex.html/
    {{person.name}}-{{person
    .age}}
这里又生成了多少个$watch呢?每个person有两个(一个name,一个age),然后ng-
repeat又有一个,因此10个person一共是(210)+1,也就是说有21个$watch。因此,每一个绑定到了DO
M上的数据都会生成一个$watch。那这写$watch是什么时候生成的呢?当我们的模版加载完毕时,也就是在linking阶段(A
ngular分为compile阶段和linking阶段),Angular解释器会寻找每个directive,然后生成每个需要的$w
atch。6.1.3$digest循环还记得我前面提到的扩展的事件循环吗?当浏览器接收到可以被angularcontext处理
的事件时,$digest循环就会触发。这个循环是由两个更小的循环组合起来的。一个处理evalAsync队列,另一个处理$watch
队列。这个是处理什么的呢?$digest将会遍历我们的$watch,然后询问:?嘿,$watch,你的值是什么??是9。?好的
,它改变过吗??没有,先生。?(这个变量没变过,那下一个)?你呢,你的值是多少??报告,是Foo。?刚才改变过没??改变过,
刚才是Bar。?(很好,我们有DOM需要更新了)?继续询问直到$watch队列都检查过。这就是所谓的dirty-checking。
既然所有的$watch都检查完了,那就要问了:有没有$watch更新过?如果有至少一个更新过,这个循环就会再次触发,直到所有的$w
atch都没有变化。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,防止无限循环。当
$digest循环结束时,DOM相应地变化。例如:/Controllercontrollers.js/app.cont
roller(''MainCtrl'',function(){$scope.name="Foo";$scope.chang
eFoo=function(){$scope.name="Bar";}});/Viewindex.html
/{{name}}Changethename>这里我们有一个$watch因为ng-click不生成$watch(函数是不会变的)。我们可以看出ng的处理流程:?我们按下按钮;
?浏览器接收到一个事件,进入angularcontext;?$digest循环开始执行,查询每个$watch是否变化;?由于监视
$scope.name的$watch报告了变化,它会强制再执行一次$digest循环;?新的$digest循环没有检测到变化;?浏
览器拿回控制权,更新与$scope.name新值相应部分的DOM。这里很重要的是每一个进入angularcontext的事件都会
执行一个$digest循环,也就是说每次我们输入一个字母循环都会检查整个页面的所有$watch。6.1.4如何进入angular
context谁决定什么事件进入angularcontext,而哪些又不进入呢?通过$apply!如果当事件触发时,你调用$ap
ply,它会进入angularcontext,如果没有调用就不会进入。现在你可能会问:刚才的例子里我也没有调用$apply啊,为
什么?Angular已经做了!因此你点击带有ng-click的元素时,时间就会被封装到一个$apply调用。如果你有一个ng-mo
del="foo"的输入框,然后你敲一个f,事件就会这样调用$apply("foo=''f'';")。Angular什么时候不会自
动为我们$apply呢?这是Angular新手共同的痛处。为什么我的jQuery不会更新我绑定的东西呢?因为jQuery没有调用$
apply,事件没有进入angularcontext,$digest循环永远没有执行。我们来看一个有趣的例子:假设我们有下面这个
directive和controller。/Controllerapp.js/app.directive(''clicka
ble'',function(){return{restrict:"E",scope:{foo:''='',bar:
''=''},template:''
  • {{
    foo}}
  • {{bar}}
  • '',link:function(scope,element,
    attrs){element.bind(''click'',function(){scope.foo++;scope.ba
    r++;});}}});app.controller(''MainCtrl'',function($scope){$scop
    e.foo=0;$scope.bar=0;});它将foo和bar从controller里绑定到一个list里面,每次点
    击这个元素的时候,foo和bar都会自增1。那我们点击元素的时候会发生什么呢?我们能看到更新吗?答案是否定的。因为点击事件是一个没
    有封装到$apply里面的常见的事件,这意味着我们会失去我们的计数吗?不会。真正的结果是:$scope确实改变了,但是没有强制$d
    igest循环,监视foo和bar的$watch没有执行。也就是说如果我们自己执行一次$apply那么这些$watch就会看见这
    些变化,然后根据需要更新DOM。执行$apply:element.bind(''click'',function(){scope
    .foo++;scope.bar++;scope.$apply();});$apply是我们的$scope(或者是direcv
    ie里的link函数中的scope)的一个函数,调用它会强制一次$digest循环(除非当前正在执行循环,这种情况下会抛出一个异常
    ,这是我们不需要在那里执行$apply的标志)。更好的使用$apply的方法:element.bind(''click'',func
    tion(){scope.$apply(function(){scope.foo++;scope.bar++;});}
    )有什么不一样的?差别就是在第一个版本中,我们是在angularcontext的外面更新的数据,如果有发生错误,Angular永
    远不知道。很明显在这个像个小玩具的例子里面不会出什么大错,但是想象一下我们如果有个alert框显示错误给用户,然后我们有个第三方的
    库进行一个网络调用然后失败了,如果我们不把它封装进$apply里面,Angular永远不会知道失败了,alert框就永远不会弹出来
    了。因此,如果你想使用一个jQuery插件,并且要执行$digest循环来更新你的DOM的话,要确保你调用了$apply。有时候我
    想多说一句的是有些人在不得不调用$apply时会“感觉不妙”,因为他们会觉得他们做错了什么。其实不是这样的,Angular不是什么
    魔术师,他也不知道第三方库想要更新绑定的数据。6.1.5使用$watch来监视你已经知道了我们设置的任何绑定都有一个它自己的$wa
    tch,当需要时更新DOM,但是我们如果要自定义自己的watches呢?简单,来看个例子:/Controllerapp.js
    /app.controller(''MainCtrl'',function($scope){$scope.name="A
    ngular";$scope.updated=-1;$scope.$watch(''name'',function(){
    $scope.updated++;});});/Viewindex.html/MainCtrl">Nameupdated:{{updated}}ti
    mes.这就是我们创造一个新的$watch的方法。第一个参数是一个字符串或者函数,在这里是只是一个字符串,就是我们要
    监视的变量的名字,在这里,$scope.name(注意我们只需要用name)。第二个参数是当$watch说我监视的表达式发生变化后
    要执行的。我们要知道的第一件事就是当controller执行到这个$watch时,它会立即执行一次,因此我们设置updated为-
    1。例子2:/Controllerapp.js/app.controller(''MainCtrl'',function(
    $scope){$scope.name="Angular";$scope.updated=0;$scope.$wa
    tch(''name'',function(newValue,oldValue){if(newValue===oldVa
    lue){return;}//AKAfirstrun$scope.updated++;});});/View
    index.html/e"/>Nameupdated:{{updated}}times.watch的第二个参数接受两个参数,新值
    和旧值。我们可以用他们来略过第一次的执行。通常你不需要略过第一次执行,但在这个例子里面你是需要的。例子3:/Controller
    app.js/app.controller(''MainCtrl'',function($scope){$scope.
    user={name:"Fox"};$scope.updated=0;$scope.$watch(''user'',
    function(newValue,oldValue){if(newValue===oldValue){retu
    rn;}$scope.updated++;});});/Viewindex.html/ller="MainCtrl">Nameupdated:{{u
    pdated}}times.我们想要监视$scope.user对象里的任何变化,和以前一样这里只是用一个对象来代替
    前面的字符串。呃?没用,为啥?因为$watch默认是比较两个对象所引用的是否相同,在例子1和2里面,每次更改$scope.name
    都会创建一个新的基本变量,因此$watch会执行,因为对这个变量的引用已经改变了。在上面的例子里,我们在监视$scope.user
    ,当我们改变$scope.user.name时,对$scope.user的引用是不会改变的,我们只是每次创建了一个新的$scope
    .user.name,但是$scope.user永远是一样的。例子4:/Controllerapp.js/app.con
    troller(''MainCtrl'',function($scope){$scope.user={name:"Fox
    "};$scope.updated=0;$scope.$watch(''user'',function(newValue,
    oldValue){if(newValue===oldValue){return;}$scope.update
    d++;},true);});/Viewindex.html/rl">Nameupdated:{{updated}}tim
    es.现在有用了吧!因为我们对$watch加入了第三个参数,它是一个bool类型的参数,表示的是我们比较的是对象的值
    而不是引用。由于当我们更新$scope.user.name时$scope.user也会改变,所以能够正确触发。6.1.6总结我希
    望你们已经学会了在Angular中数据绑定是如何工作的。我猜想你的第一印象是dirty-checking很慢,好吧,其实是不对的。
    它像闪电般快。但是,如果你在一个模版里有2000-3000个watch,它会开始变慢。但是我觉得如果你达到这个数量级,就可以找个用
    户体验专家咨询一下了。无论如何,随着ECMAScript6的到来,在Angular未来的版本里我们将会有Object.observ
    e那样会极大改善$digest循环的速度。6.2自定义指令详解angular的指令机制。angular通过指令的方式实现了HTML
    的扩展,增强后的HTML不仅长相焕然一新,同时也获得了很多强大的技能。更厉害的是,你还可以自定义指令,这就意味着HTML标签的范围
    可以扩展到无穷大。angular赋予了你造物主的能力。既然是作为angular的精华之一,相应的指令相关的知识也很多的。6.2.1
    指令的编译过程在开始自定义指令之前,我们有必要了解一下指令在框架中的执行流程:1.浏览器得到HTML字符串内容,解析得到D
    OM结构。2.ng引入,把DOM结构扔给$compile函数处理:①找出DOM结构中有变量占位符;②匹配找出
    DOM中包含的所有指令引用;③把指令关联到DOM;④关联到DOM的多个指令按权重排列;⑤执行指令中的compile
    函数(改变DOM结构,返回link函数);⑥得到的所有link函数组成一个列表作为$compile函数的返回。
    3.执行link函数(连接模板的scope)。这里注意区别一下$compile和compile,前者是ng内部的编译服务,
    后者是指令中的编译函数,两者发挥作用的范围不同。compile和link函数息息相关又有所区别,这个在后面会讲。了解执行流程对后面
    的理解会有帮助。在这里有些人可能会问,angular不就是一个js框架吗,怎么还能跟编译扯上呢,又不是像C++那样的高级语言。其实
    此编译非彼编译,ng编译的工作是解析指令、绑定监听器、替换模板中的变量等。因为工作方式很像高级语言编辑中的递归、堆栈过程,所以起名
    为编译,不要疑惑。6.2.2指令的使用方式及命名方法指令的几种使用方式如下:作为标签:作为属性
    作为注释:作为类
    名:其实常用的就是作为标签和属性,下面两种用法目前还没见过,
    感觉就是用来卖萌的,姑且留个印象。我们自定义的指令就是要支持这样的用法。关于自定义指令的命名,你可以随便怎么起名字都行,官方是推荐
    用[命名空间-指令名称]这样的方式,像ng-controller。不过你可千万不要用ng-前缀了,防止与系统自带的指令重名。另外一
    个需知道的地方,指令命名时用驼峰规则,使用时用-分割各单词。如:定义myDirective,使用时像这样:e>。6.2.3自定义指令的配置参数下面是定义一个标准指令的示例,可配置的参数包括以下部分:myModule.directive(
    ''namespaceDirectiveName'',functionfactory(injectables){vardir
    ectiveDefinitionObject={restrict:string,//指令的使用方式,包括标签,属性,类,注
    释priority:number,//指令执行的优先级template:string,//指令使用的模板,用HTML字符串
    的形式表示templateUrl:string,//从指定的url地址加载模板replace:bool,//是否用模板替换
    当前元素,若为false,则append在当前元素上transclude:bool,//是否将当前元素的内容转移到模板中sc
    ope:boolorobject,//指定指令的作用域controller:functioncontrollerCon
    structor($scope,$element,$attrs,$transclude){...},//定义与其他指令进行交
    互的接口函数require:string,//指定需要依赖的其他指令link:functionpostLink(scope
    ,iElement,iAttrs){...},//以编程的方式操作DOM,包括添加监听器等compile:functio
    ncompile(tElement,tAttrs,transclude){return:{pre:function
    preLink(scope,iElement,iAttrs,controller){...},post:function
    postLink(scope,iElement,iAttrs,controller){...}}}//编程的方式修改D
    OM模板的副本,可以返回链接函数};returndirectiveDefinitionObject;});看上去好复杂的样子
    ,定义一个指令需要这么多步骤嘛?当然不是,你可以根据自己的需要来选择使用哪些参数。事实上priority和compile用的比较少
    ,template和templateUrl又是互斥的,两者选其一即可。所以不必紧张,接下来分别学习一下这些参数:指令的表现配置参数
    :restrict、template、templateUrl、replace、transclude;指令的行为配置参数:compi
    le和link;指令划分作用域配置参数:scope;指令间通信配置参数:controller和require。6.2.3指令的表现
    参数restrict等指令的表现配置参数:restrict、template、templateUrl、replace、transc
    lude。我将先从一个简单的例子开始。例子的代码如下:varapp=angular.module(''MyApp'',[],
    function(){console.log(''here'')});app.directive(''sayHello'',functi
    on(){return{restrict:''E'',template:''
    hello
    ''};})然后在
    页面中,我们就可以使用这个名为sayHello的指令了,它的作用就是输出一个hello单词。像这样使用:say-hello>这样页面就会显示出hello了,看一下生成的代码:
    hello
    say-hello>稍稍解释一下我们用到的两个参数,restirct用来指定指令的使用类型,其取值及含义如下:取值含义使用示
    例E标签A属性div>C类M注释默认值是A。也可以使用这些值的组合,如EA,EC等等。我们这里指定为E,那么它就可以像标签一样使用了。如
    果指定为A,我们使用起来应该像这样:从生成的代码中,你也看到了template的作用,它
    就是描述你的指令长什么样子,这部分内容将出现在页面中,即该指令所在的模板中,既然是模板中,template的内容中也可以使用ng-
    modle等其他指令,就像在模板中使用一样。在上面生成的代码中,我们看到了
    hello
    外面还包着一层hello>标签,如果我们不想要这一层多余的东西了,replace就派上用场了,在配置中将replace赋值为true,将得到如下
    结构:
    hello
    replace的作用正如其名,将指令标签替换为了temple中定义的内容。不写的话默认为
    false。上面的template未免也太简单了,如果你的模板HTML较复杂,如自定义一个ui组件指令,难道要拼接老长的字符串?当
    然不需要,此时只需用templateUrl便可解决问题。你可以将指令的模板单独命名为一个html文件,然后在指令定义中使用temp
    lateUrl指定好文件的路径即可,如:templateUrl:‘helloTemplate.html’系统会自动发一个ht
    tp请求来获取到对应的模板内容。是不是很方便呢,你不用纠结于拼接字符串的烦恼了。如果你是一个追求完美的有考虑性能的工程师,可能会发
    问:那这样的话岂不是要牺牲一个http请求?这也不用担心,因为ng的模板还可以用另外一种方式定义,那就是使用你可以把这段代码写在页面头部,这样就不必去请求它了。在实际项目中,你也可以将所
    有的模板内容集中在一个文件中,只加载一次,然后根据id来取用。接下来我们来看另一个比较有用的配置:transclude,定义是否将
    当前元素的内容转移到模板中。看解释有点抽象,不过亲手试试就很清楚了,看下面的代码(例06):app.directive(''sayH
    ello'',function(){return{restrict:''E'',template:''
    hello,<
    bng-transclude>!
    '',replace:true,transclude:true}
    ;})指定了transclude为true,并且template修改了一下,加了一个标签,并在上面使用了ng-transcl
    ude指令,用来告诉指令把内容转移到的位置。那我们要转移的内容是什么呢?请看使用指令时的变化:美女-hello>内容是什么你也看到了哈~在运行的时候,美女将会被转移到标签中,原来此配置的作用就是——乾坤大挪移!看效果:he
    llo,美女!这个还是很有用的,因为你定义的指令不可能老是那么简单,只有一个空标签。当你需要对指令中的内容进行处理时,此参数便大
    有可用。6.2.4指令的行为参数:compile和link6.2.3中简单介绍了自定义一个指令的几个简单参数,restrict、t
    emplate、templateUrl、replace、transclude,这几个理解起来相对容易很多,因为它们只涉及到了表现,
    而没有涉及行为。我们继续学习ng自定义指令的几个重量级参数:compile和link理解compile和link不知大家有没有这
    样的感觉,自己定义指令的时候跟写jQuery插件有几分相似之处,都是先预先定义好页面结构及监听函数,然后在某个元素上调用一下,该元
    素便拥有了特殊的功能。区别在于,jQuery的侧重点是DOM操作,而ng的指令中除了可以进行DOM操作外,更注重的是数据和模板的绑
    定。jQuery插件在调用的时候才开始初始化,而ng指令在页面加载进来的时候就被编译服务($compile)初始化好了。在指令定义
    对象中,有compile和link两个参数,它们是做什么的呢?从字面意义上看,编译、链接,貌似太抽象了点。其实可大有内涵,为了在自
    定义指令的时候能正确使用它们,现在有必要了解一下ng是如何编译指令的。指令的解析流程详解我们知道ng框架会在页面载入完毕的时候,
    根据ng-app划定的作用域来调用$compile服务进行编译,这个$compile就像一个大总管一样,清点作用域内的DOM元素,
    看看哪些元素上使用了指令(如),或者哪些元素本身就是个指令(如<
    /mydirec>),或者使用了插值指令({{}}也是一种指令,叫interpolationdirective),$compi
    le大总管会把清点好的财产做一个清单,然后根据这些指令的优先级(priority)排列一下,真是个细心的大总管哈~大总管还会根据指
    令中的配置参数(template,place,transclude等)转换DOM,让指令“初具人形”。然后就开始按顺序执行各指令的
    compile函数,注意此处的compile可不是大总管$compile,人家带着$是土豪,此处执行的compile函数是我们指令
    中配置的,compile函数中可以访问到DOM节点并进行操作,其主要职责就是进行DOM转换,每个compile函数执行完后都会返回
    一个link函数,这些link函数会被大总管汇合一下组合成一个合体后的link函数,为了好理解,我们可以把它想象成葫芦小金刚,就像
    是进行了这样的处理。//合体后的link函数functionAB(){A();//子link函数B();//子link函
    数}接下来进入link阶段,合体后的link函数被执行。所谓的链接,就是把view和scope链接起来。链接成啥样呢?就是我们熟
    悉的数据绑定,通过在DOM上注册监听器来动态修改scope中的数据,或者是使用$watchs监听scope中的变量来修改DOM,
    从而建立双向绑定。由此也可以断定,葫芦小金刚可以访问到scope和DOM节点。不要忘了我们在定义指令中还配置着一个link参数呢,
    这么多link千万别搞混了。那这个link函数是干嘛的呢,我们不是有葫芦小金刚了嘛?那我告诉你,其实它是一个小三。此话怎讲?com
    pile函数执行后返回link函数,但若没有配置compile函数呢?葫芦小金刚自然就不存在了。正房不在了,当然就轮到小三出马了,
    大总管$compile就把这里的link函数拿来执行。这就意味着,配置的link函数也可以访问到scope以及DOM节点。值得注意
    的是,compile函数通常是不会被配置的,因为我们定义一个指令的时候,大部分情况不会通过编程的方式进行DOM操作,而更多的是进行
    监听器的注册、数据的绑定。所以,小三名正言顺的被大总管宠爱。听完了大总管、葫芦小金刚和小三的故事,你是不是对指令的解析过程比较清晰
    了呢?不过细细推敲,你可能还是会觉得情节生硬,有些细节似乎还是没有透彻的明白,所以还需要再理解下面的知识点:compile和lin
    k的区别其实在我看完官方文档后就一直有疑问,为什么监听器、数据绑定不能放在compile函数中,而偏偏要放在link函数中?为什
    么有了compile还需要link?就跟你质疑我编的故事一样,为什么最后小三被宠爱了?所以我们有必要探究一下,compile和li
    nk之间到底有什么区别。好,正房与小三的PK现在开始。首先是性能。举个例子:
      ray">
    我们的观察目标是ng-repeat指令。假设一
    个前提是不存在link。大总管$compile在编译这段代码时,会查找到ng-repeat,然后执行它的compile函数,com
    pile函数根据array的长度复制出n个
  • 标签。而复制出的
  • 节点中还有节点并且使用了ng-modle指
    令,所以compile还要扫描它并匹配指令,然后绑定监听器。每次循环都做如此多的工作。而更加糟糕的一点是,我们会在程序中向arra
    y中添加元素,此时页面上会实时更新DOM,每次有新元素进来,compile函数都把上面的步骤再走一遍,岂不是要累死了,这样性能必然
    不行。现在扔掉那个假设,在编译的时候compile就只管生成DOM的事,碰到需要绑定监听器的地方先存着,有几个存几个,最后把它们汇
    总成一个link函数,然后一并执行。这样就轻松多了,compile只需要执行一次,性能自然提升。另外一个区别是能力。尽管compi
    le和link所做的事情差不多,但它们的能力范围还是不一样的。比如正房能管你的存款,小三就不能。小三能给你初恋的感觉,正房却不能。
    我们需要看一下compile函数和link函数的定义:functioncompile(tElement,tAttrs,tra
    nsclude){...}functionlink(scope,iElement,iAttrs,controller
    ){...}这些参数都是通过依赖注入而得到的,可以按需声明使用。从名字也容易看出,两个函数各自的职责是什么,compile可
    以拿到transclude,允许你自己编程管理乾坤大挪移的行为。而link中可以拿到scope和controller,可以与sco
    pe进行数据绑定,与其他指令进行通信。两者虽然都可以拿到element,但是还是有区别的,看到各自的前缀了吧?compile拿到的
    是编译前的,是从template里拿过来的,而link拿到的是编译后的,已经与作用域建立了关联,这也正是link中可以进行数据绑定
    的原因。我暂时只能理解到这个程度了。实在不想理解这些知识的话,只要简单记住一个原则就行了:如果指令只进行DOM的修改,不进行数据
    绑定,那么配置在compile函数中,如果指令要进行数据绑定,那么配置在link函数中。6.2.5指令的划分作用域参数:scope
    我们在上面写了一个简单的,能够跟美女打招呼。但是看看人家ng内置的指令,都是这么用的
    :ng-model=”m”,ng-repeat=”ainarray”,不单单是作为属性,还可以赋值给它,与作用域中的一个变量绑
    定好,内容就可以动态变化了。假如我们的sayHello可以这样用:美女ay-hello>,把要对美女说的话写在一个变量content中,然后只要在controller中修改content的值,页面就可
    以显示对美女说的不同的话。这样就灵活多了,不至于见了美女只会说一句hello,然后就没有然后。为了实现这样的功能,我们需要使用sc
    ope参数,下面来介绍一下。使用scope为指令划分作用域顾名思义,scope肯定是跟作用域有关的一个参数,它的作用是描述指令与
    父作用域的关系,这个父作用域是指什么呢?想象一下我们使用指令的场景,页面结构应该是这个样子:"testC">美女外层肯定会有一个
    controller,而在controller的定义中大体是这个样子:varapp=angular.module(''MyAp
    p'',[],function(){console.log(''here'')});app.controller(''testC'',f
    unction($scope){$scope.content=''今天天气真好!'';});所谓sayHello的父作用域就是这
    个名叫testC的控制器所管辖的范围,指令与父作用域的关系可以有如下取值:取值说明false默认值。使用父作用域作为自己的作用域t
    rue新建一个作用域,该作用域继承父作用域javascript对象与父作用域隔离,并指定可以从父作用域访问的变量乍一看取值为fal
    se和true好像没什么区别,因为取值为true时会继承父作用域,即父作用域中的任何变量都可以访问到,效果跟直接使用父作用域差不多
    。但细细一想还是有区别的,有了自己的作用域后就可以在里面定义自己的东西,与跟父作用域混在一起是有本质上的区别。好比是父亲的钱你想花
    多少花多少,可你自己挣的钱父亲能花多少就不好说了。你若想看这两个作用域的区别,可以在link函数中打印出来看看,还记得link函数
    中可以访问到scope吧。最有用的还是取值为第三种,一个对象,可以用键值来显式的指明要从父作用域中使用属性的方式。当scope值为
    一个对象时,我们便建立了一个与父层隔离的作用域,不过也不是完全隔离,我们可以手工搭一座桥梁,并放行某些参数。我们要实现对美女说各种
    话就得靠这个。使用起来像这样:scope:{attributeName1:''BINDING_STRATEGY'',attri
    buteName2:''BINDING_STRATEGY'',...}键为属性名称,值为绑定策略。等等!啥叫绑定策略?最讨厌冒新名
    词却不解释的行为!别急,听我慢慢道来。先说属性名称吧,你是不是认为这个attributeName1就是父作用域中的某个变量名称?
    错!其实这个属性名称是指令自己的模板中要使用的一个名称,并不对应父作用域中的变量,稍后的例子中我们来说明。再来看绑定策略,它的取值
    按照如下的规则:符号说明举例@传递一个字符串作为属性的值str:‘@string’=使用父作用域中的一个属性,绑定数据到指令的
    属性中name:‘=username’&使用父作用域中的一个函数,可以在指令中调用getName:‘&getUserNam
    e’总之就是用符号前缀来说明如何为指令传值。你肯定迫不及待要看例子了,我们结合例子看一下,小二,上栗子~举例说明我想要实现上面想
    像的跟美女多说点话的功能,即我们给sayHello指令加一个属性,通过给属性赋值来动态改变说话的内容主要代码如下:app.con
    troller(''testC'',function($scope){$scope.content=''今天天气真好!'';});a
    pp.directive(''sayHello'',function(){return{restrict:''E'',templ
    ate:''
    hello,,{{cont}}
    '',replace
    :true,transclude:true,scope:{cont:''=speak''}};});然后在模
    板中,我们如下使用指令:t">美女看看运行效果:美女今天天气真好!执行的流程是这样的:①指令被编译的时候会扫描
    到template中的{{cont}},发现是一个表达式;②查找scope中的规则:通过speak与父作用域绑定,方式是传
    递父作用域中的属性;③speak与父作用域中的content属性绑定,找到它的值“今天天气真好!”;④将content的值
    显示在模板中。这样我们说话的内容content就跟父作用域绑定到了一其,如果动态修改父作用域的content的值,页面上的内容就
    会跟着改变,正如你点击“换句话”所看到的一样。这个例子也太小儿科了吧!简单虽简单,但可以让我们理解清楚,为了检验你是不是真的明白
    了,可以思考一下如何修改指令定义,能让sayHello以如下两种方式使用:nt">美女美女答案我就不说了,简单的很。下面
    有更重要的事情要做,我们说好了要写一个真正能用的东西来着。接下来就结合所学到的东西来写一个折叠菜单,即点击可展开,再点击一次就收缩
    回去的菜单。控制器及指令的代码如下(例07):app.controller(''testC'',function($scope){$
    scope.title=''个人简介'';$scope.text=''大家好,我是一名前端工程师,我正在研究AngularJs
    ,欢迎大家与我交流'';});app.directive(''expander'',function(){return{rest
    rict:''E'',templateUrl:''expanderTemp.html'',replace:true,tr
    ansclude:true,scope:{mytitle:''=etitle''},link:function
    (scope,element,attris){scope.showText=false;scope.toggleText
    =function(){scope.showText=!scope.showText;}}};});HTML中的
    代码如下:class="mybox">{{my
    title}}script>{{tex
    t}}还是比较容易看懂的,我只做一点必要的解释。首先我们定义模板的时候使用了ng的一种定义方
    ,在指令中就可以用t
    emplateUrl根据这个id来找到模板。指令中的{{mytitle}}表达式由scope参数指定从etitle传递,etitl
    e指向了父作用域中的title。为了实现点击标题能够展开收缩内容,我们把这部分逻辑放在了link函数中,link函数可以访问到指令
    的作用域,我们定义showText属性来表示内容部分的显隐,定义toggleText函数来进行控制,然后在模板中绑定好。如果把s
    howText和toggleText定义在controller中,作为$scope的属性呢?显然是不行的,这就是隔离作用域的意义所
    在,父作用域中的东西除了title之外通通被屏蔽。上面的例子中,scope参数使用了=号来指定获取属性的类型为父作用域的属性,如果
    我们想在指令中使用父作用域中的函数,使用&符号即可,是同样的原理。6.2.6指令间通信参数:controller和require
    使用指令来定义一个ui组件是个不错的想法,首先使用起来方便,只需要一个标签或者属性就可以了,其次是可复用性高,通过controll
    er可以动态控制ui组件的内容,而且拥有双向绑定的能力。当我们想做的组件稍微复杂一点,就不是一个指令可以搞定的了,就需要指令与指令
    的协作才可以完成,这就需要进行指令间通信。想一下我们进行模块化开发的时候的原理,一个模块暴露(exports)对外的接口,另外一个
    模块引用(require)它,便可以使用它所提供的服务了。ng的指令间协作也是这个原理,这也正是自定义指令时controller参
    数和require参数的作用。controller参数用于定义指令对外提供的接口,它的写法如下:controller:funct
    ioncontrollerConstructor($scope,$element,$attrs,$transclude)
    它是一个构造器函数,将来可以构造出一个实例传给引用它的指令。为什么叫controller(控制器)呢?其实就是告诉引用它的指令,你
    可以控制我。至于可以控制那些东西呢,就需要在函数体中进行定义了。先看controller可以使用的参数,作用域、节点、节点的属性、
    节点内容的迁移,这些都可以通过依赖注入被传进来,所以你可以根据需要只写要用的参数。关于如何对外暴露接口,我们在下面的例子来说明。r
    equire参数便是用来指明需要依赖的其他指令,它的值是一个字符串,就是所依赖的指令的名字,这样框架就能按照你指定的名字来从对应的
    指令上面寻找定义好的controller了。不过还稍稍有点特别的地方,为了让框架寻找的时候更轻松些,我们可以在名字前面加个小小的前
    缀:^,表示从父节点上寻找,使用起来像这样:require:‘^directiveName’,如果不加,$compile服务只
    会从节点本身寻找。另外还可以使用前缀:?,此前缀将告诉$compile服务,如果所需的controller没找到,不要抛出异常。所
    需要了解的知识点就这些,接下来是例子时间,依旧是从书上抄来的一个例子,我们要做的是一个手风琴菜单,就是多个折叠菜单并列在一起,此例
    子用来展示指令间的通信再合适不过。首先我们需要定义外层的一个结构,起名为accordion,代码如下:app.directive(
    ''accordion'',function(){return{restrict:''E'',template:''ng-transclude>'',replace:true,transclude:true,contro
    ller:function(){varexpanders=[];this.gotOpended=function(
    selectedExpander){angular.forEach(expanders,function(e){if(sele
    ctedExpander!=e){e.showText=false;}});}this.addExpander
    =function(e){expanders.push(e);}}}});需要解释的只有controller中的代码,
    我们定义了一个折叠菜单数组expanders,并且通过this关键字来对外暴露接口,提供两个方法。gotOpended接受一个se
    lectExpander参数用来修改数组中对应expander的showText属性值,从而实现对各个子菜单的显隐控制。addEx
    pander方法对外提供向expanders数组增加元素的接口,这样在子菜单的指令中,便可以调用它把自身加入到accordion中
    。看一下我们的expander需要做怎样的修改呢:app.directive(''expander'',function(){ret
    urn{restrict:''E'',templateUrl:''expanderTemp.html'',replace
    :true,transclude:true,require:''^?accordion'',scope:{tit
    le:''=etitle''},link:function(scope,element,attris,accordionC
    ontroller){scope.showText=false;accordionController.addExpand
    er(scope);scope.toggleText=function(){scope.showText=!scop
    e.showText;accordionController.gotOpended(scope);}}};});首先使用
    require参数引入所需的accordion指令,添加?^前缀表示从父节点查找并且失败后不抛出异常。然后便可以在link函数中使
    用已经注入好的accordionController了,调用addExpander方法将自己的作用域作为参数传入,以供accord
    ionController访问其属性。然后在toggleText方法中,除了要把自己的showText修改以外,还要调用accor
    dionController的gotOpended方法通知父层指令把其他菜单给收缩起来。指令定义好后,我们就可以使用了,使用起来如
    下:expander.title">{{expander.text}}外层使用了a
    ccordion指令,内层使用expander指令,并且在expander上用ng-repeat循环输出子菜单。请注意这里遍历的数
    组expanders可不是accordion中定义的那个expanders,如果你这么认为了,说明还是对作用域不够了解。此expa
    nders是ng-repeat的值,它是在外层controller中的,所以,在testC中,我们需要添加如下数据:$scope.
    expanders=[{title:''个人简介'',text:''大家好,我是一名前端工程师,我正在研究AngularJs
    ,欢迎大家与我交流''},{title:''我的爱好'',text:''LOL''},{title:''性格'',text:''
    我的性格就是无性格''}];6.3性能及调优6.3.1性能测试AnglarJS作为一款优秀的Web框架,可大大简化前端开发的
    负担。AnglarJS很棒,但当处理包含复杂数据结构的大型列表时,其运行速度就会非常慢。这是我们将核心管理页面迁移到Angular
    JS过程中遇到的问题。这些页面在显示500行数据时本应该工作顺畅,但首个方法的渲染时间竟花费了7秒,太可怕了。后来,我们发现了在实
    现过程中存在两个主要性能问题。一个与“ng-repeat”指令有关,另一个与过滤器有关。AngularJS中的ng-repea
    t在处理大型列表时,速度为什么会变慢?AngularJS中的ng-repeat在处理2500个以上的双向数据绑定时速度会变慢。这
    是由于AngularJS通过“dirtychecking”函数来检测变化。每次检测都会花费时间,所以包含复杂数据结构的大型列表将
    降低你应用的运行速度。提高性能的先决条件时间记录指令为了测量一个列表渲染所花费的时间,我们写了一个简单的程序,通过使用“ng-
    repeat”的属性“$last”来记录时间。时间存放在TimeTracker服务中,这样时间记录就与服务器端的数据加载分开了。/
    /Postrepeatdirectiveforloggingtherenderingtimeangular.mod
    ule(''siApp.services'').directive(''postRepeatDirective'',[''$timeout
    '',''$log'',''TimeTracker'',function($timeout,$log,TimeTracker)
    {returnfunction(scope,element,attrs){if(scope.$last){
    $timeout(function(){vartimeFinishedLoadingList=TimeTracke
    r.reviewListLoaded();varref=newDate(timeFinishedLoadingLis
    t);varend=newDate();$log.debug("##DOMrenderinglistt
    ook:"+(end-ref)+"ms");});}};}]);//Usein
    HTML:
    Chrome开发者工具的时间轴(Timeline)属性在Chrome开发者工具的时间轴标签中,你可以看见事件、每秒内浏览器帧数和
    内存分配。“memory”工具用来检测内存泄漏,及页面所需的内存。当帧速率每秒低于30帧时就会出现页面闪烁问题。“frames”工具可帮助了解渲染性能,还可显示出一个JavaScript任务所花费的CPU时间。通过限制列表的大小进行基本的调优缓解该问题,最好的办法是限制所显示列表的大小。可通过分页、添加无限滚动条来实现。分页,我们可以使用AngularJS的“limitTo”过滤器(AngularJS1.1.4版本以后)和“startFrom”过滤器。可以通过限制显示列表的大小来减少渲染时间。这是减少渲染时间最高效的方法。6.3.2七大调优法则1.渲染没有数据绑定的列表这是最明显的解决方案,因为数据绑定是性能问题最可能的根源。如果你只想显示一次列表,并不需要更新、改变数据,放弃数据绑定是绝佳的办法。不过可惜的是,你会失去对数据的控制权,但除了该法,我们别无选择。2.不要使用内联方法计算数据为了在控制器中直接过滤列表,不要使用可获得过滤链接的方法。“ng-repeat”会评估每个表达式。在我们的案例中,“filteredItems()”返回过滤链接。如果评估过程很慢,它将迅速降低整个应用的速度。//这并不是一个好方法,因为要频繁地评估。//这是要采用的方法3.使用两个列表(一个用来进行视图显示,一个作为数据源)将要显示的列表与总的数据列表分开,是非常有用的模型。你可以对一些过滤进行预处理,并将存于缓存中的链接应用到视图上。下面案例展示了基本实现过程。filteredLists变量保存着缓存中的链接,applyFilter方法来处理映射。/Controller///Basiclistvaritems=[{name:"John",active:true},{name:"Adam"},{name:"Chris"},{name:"Heather"}];//InitdisplayedList$scope.displayedItems=items;//FilterCachevarfilteredLists[''active'']=$filter(''filter)(items,{"active":true});//Applythefilter$scope.applyFilter=function(type){if(filteredLists.hasOwnProperty(type){//Checkiffilteriscached$scope.displayedItems=filteredLists[type];}else{/Noncachedfiltering/}}//Resetfilter$scope.resetFilter=function(){$scope.displayedItems=items;}/View/Selectactive
      {{item.name}}
    4.在其他模板中使用ng-if来代替ng-show如果你用指令、模板来渲染额外的信息,例如通过点击来显示列表项的详细信息,一定要使用ng-if(AngularJSv.1.1.5以后)。ng-if可阻止渲染(与ng-show相比)。所以其它DOM和数据绑定可根据需要进行评估。

    {{item.title}}

    Showdetails{{item.details}}
  • 5.不要使用ng-mouseenter、ng-mouseleave等指令使用内部指令,像ng-mouseenter,AngularJS会使你的页面闪烁。浏览器的帧速率通常低于每秒30帧。使用jQuery创建动画、鼠标悬浮效果可以解决该问题。确保将鼠标事件放入jQuery的.live()函数中。6.关于过滤的小提示:通过ng-show隐藏多余的元素对于长列表,使用过滤同样会减低工作效率,因为每个过滤都会创建一个原始列表的子链接。在很多情况下,数据没有变化,过滤结果也会保持不变。所以对数据列表进行预过滤,并根据情况将它应用到视图中,会大大节约处理时间。在ng-repeat指令中使用过滤器,每个过滤器会返回一个原始链接的子集。AngularJS从DOM中移除多余元素(通过调用$destroy),同时也会从$scope中移除他们。当过滤器的输入发生改变时,子集也会随着变化,元素必须进行重新链接,或着再调用$destroy。大部分情况下,这样做很好,但一旦用户经常过滤,或者列表非常巨大,不断的链接与销毁将影响性能。为了加快过滤的速度,你可以使用ng-show和ng-hide指令。在控制器中,进行过滤,并为每项添加一个属性。依靠该属性来触发ng-show。结果是,只为这些元素增加ng-hide类,来代替将它们移除子列表、$scope和DOM。触发ng-show的方法之一是使用表达式语法。ng-show的值由表达式语法来确定。可以看下面的例子:{{item.name}}7.关于过滤的小提示:防抖动输入解决第6点提出的持续过滤问题的另一个方法是防抖动用户输入。例如,如果用户输入一个搜索关键词,只当用户停止输入后,过滤器才会被激活。使用该防抖动服务的一个很好的解决方案请见:http://jsfiddle.net/Warspawn/6K7Kd/。将它应用到你的视图及控制器中,如下所示:/Controller///WatchthequeryInputanddebouncethefilteringby350ms.$scope.$watch(''queryInput'',function(newValue,oldValue){if(newValue===oldValue){return;}$debounce(applyQuery,350);});varapplyQuery=function(){$scope.filter.query=$scope.query;};/View/{{item.title}}总结angular上手比较难,初学者(特别是习惯了使用JQuery的人)可能不太适应其语法以及思想。随着对ng探索的一步步深入,也确实感觉到了这一点,尤其是框架内部的某些执行机制。7.1页面效果ng-showng-hide无动画效果问题7.2委派事件(代理事件)7.2.1NG循环及事件绑定
    Ng会根据array的长度复制出n个
  • 标签。而复制出的
  • 节点中还有节点并且使用了ng-modle指令,所以ng会对所有的绑定监听器(事件)。如果array很大,就会绑定太多的事件,性能出现问题。7.2.2jQuery委派事件从jQuery1.7开始,提供了.on()附加事件处理程序。.on(events[,selector][,data],handler(eventObject))参数Selector为一个选择器字符串,用于过滤出被选中的元素中能触发事件的后代元素。如果选择器是null或者忽略了该选择器,那么被选中的元素总是能触发事件。如果省略selector或者是null,那么事件处理程序被称为直接事件或者直接绑定事件。每次选中的元素触发事件时,就会执行处理程序,不管它直接绑定在元素上,还是从后代(内部)元素冒泡到该元素的。当提供selector参数时,事件处理程序是指为委派事件(代理事件)。事件不会在直接绑定的元素上触发,但当selector参数选择器匹配到后代(内部元素)的时候,事件处理函数才会被触发。jQuery会从eventtarget开始向上层元素(例如,由最内层元素到最外层元素)开始冒泡,并且在传播路径上所有绑定了相同事件的元素若满足匹配的选择器,那么这些元素上的事件也会被触发。委托事件有两个优势:他们能在后代元素添加到文档后,可以处理这些事件;代理事件的另一个好处就是,当需要监视很多元素的时候,代理事件的开销更小。例如,在一个表格的tbody中含有1,000行,下面这个例子会为这1,000元素绑定事$("#dataTabletbodytr").on("click",function(event){alert($(this).text());});委派事件的方法只有一个元素的事件处理程序,tbody,并且事件只会向上冒泡一层(从被点击的tr到tbody):$("#dataTabletbody").on("click","tr",function(event){alert($(this).text());});许多委派的事件处理程序绑定到document树的顶层附近,可以降低性能。每次发生事件时,jQuery需要比较从eventtarget(目标元素)开始到文档顶部的路径中每一个元素上所有该类型的事件。为了获得更好的性能,在绑定代理事件时,绑定的元素最好尽可能的靠近目标元素。避免在大型文档中,过多的在document或document.body上添加代理事件。AngularJS技术参考手册2
  • AngularJS介绍AngularJS学习文档目录1前言32AngularJS概述和使用32.1AngularJS是什么?32.2
    AngularJS简单介绍42.3什么时候该用AngularJS52.4下载AngularJS并加入项目53AugularJ
    S特性183.1特性一:双向的数据绑定193.2特性二:模板203.3特性三:MVC213.4特性四:服务和依赖注入213
    .5特性五:指令(Directives)224功能介绍224.1数据绑定224.2scopes、module、contro
    ller234.2.1scopes234.2.2module244.2.3ng-controller244.3ajax25
    4.4表达式254.5过滤器264.5.1过滤器使用方式264.5.2ng的内置过滤器274.5.3自定义过滤器及示例294
    .6指令(directive)304.6.1样式相关的指令304.6.2表单控件功能相关指令314.6.3事件绑定相关指令
    344.6.4特殊的ng-src和ng-href354.6.5自定义指令示例364.7服务(service)374.7.1
    服务介绍374.7.2自定义服务374.7.3管理服务的依赖关系384.7.4自定义服务示例394.8依赖注入DI414.
    9路由(route)424.9.1ngRoute内容424.9.2ng的路由机制424.9.3路由示例434.10NG动
    画效果454.10.1NG动画效果简介454.10.2动画效果示例465功能演示475.1数据绑定475.2控制器485
    .2.1index.html内容485.2.2invoice1.js内容495.3服务(Service)505.3.1in
    dex.html内容505.3.2finance2.js内容515.3.3invoice2.js内容515.4调用WebA
    PI526AngularJS进阶526.1数据绑定原理研究526.1.1AngularJS扩展事件循环526.1.2$wat
    ch队列526.1.3$digest循环536.1.4如何进入angularcontext546.1.5使用$watch来监
    视566.1.6总结586.2自定义指令详解586.2.1指令的编译过程586.2.2指令的使用方式及命名方法596.2.3自定
    义指令的配置参数596.2.3指令的表现参数restrict等606.2.4指令的行为参数:compile和link616.2.5
    指令的划分作用域参数:scope636.2.6指令间通信参数:controller和require676.3性能及调优696.3
    .1性能测试696.3.2七大调优法则707总结727.1页面效果727.2委派事件(代理事件)727.2.1NG循环及事件绑
    定727.2.2jQuery委派事件731前言前端技术的发展是如此之快,各种优秀技术、优秀框架的出现简直让人目不暇接,紧跟时代
    潮流,学习掌握新知识自然是不敢怠慢。AngularJS是google在维护,其在国外已经十分火热,可是国内的使用情况却有不小的差距
    ,参考文献/网络文章也很匮乏。这里便将我学习AngularJS写成文档,一方面作为自己学习路程上的记录,另一方面也给有兴趣的同学一
    些参考。首先我自己也是一名学习者,会以学习者的角度来整理我的行文思路,这里可能只是些探索,有理解或是技术上的错误还请大家指出;其次
    我特别喜欢编写小例子来把一件事情说明白,故在文中会尽可能多的用示例加代码讲解,我相信这会是一种比较好的方式;最后,我深知Angul
    arJS的使用方式跟jquery的使用方式有很大不同,在大家都有jquery、ext经验的条件下对于angular的学习会困难重重
    ,不过我更相信在大家的坚持下,能够快速的学好AngularJS,至少咱也能深入了解到AngularJS的基本思想,对咱们以后自己的
    插件开发、项目开发都会有很大的启示。2AngularJS概述和使用2.1AngularJS是什么?AngularJS诞生于2
    009年,由MiskoHevery等人(一帮热血青年)创建,后为Google所收购。是一款优秀的前端JS框架,已经被用于Goo
    gle的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、依赖注入等等。目前企业开发
    使用最多的是版本一。AngularJs(后面就简称ng了)是一个用于设计动态Web应用的结构框架。首先,它是一个框架,不是类库,是
    像EXT一样提供一整套方案用于设计Web应用。它不仅仅是一个javascript框架,因为它的核心其实是对HTML标签的增强。何为
    HTML标签增强?其实就是使你能够用标签完成一部分页面逻辑,具体方式就是通过自定义标签、自定义属性等,这些HTML原生没有的标签/
    属性在ng中有一个名字:指令(directive)。后面会详细介绍。那么,什么又是动态Web应用呢?与传统web系统相区别,Web
    应用能为用户提供丰富的操作,能够随用户操作不断更新视图而不进行url跳转。ng官方也声明它更适用于开发CRUD应用,即数据操作比较
    多的应用,而非是游戏或图像处理类应用。为了实现这些,ng引入了一些非常好的特性,包括模板机制、数据绑定、模块、指令、依赖注入、路由
    。通过数据与模板的绑定,能够让我们摆脱繁琐的DOM操作,而将注意力集中在业务逻辑上。另外一个疑问,ng是MVC框架吗?还是MVV
    M框架?官网有提到ng的设计采用了MVC的基本思想,而又不完全是MVC,因为在书写代码时我们确实是在用ng-controller这
    个指令(起码从名字上看,是MVC吧),但这个controller处理的业务基本上都是与view进行交互,这么看来又很接近MVVM。
    让我们把目光移到官网那个非醒目的title上:“AngularJS—SuperheroicJavaScriptMVWFr
    amework”。2.2AngularJS简单介绍AngularJS重新定义了前端应用的开发方式。面对HTML和JavaScr
    ipt之间的界线,它非但不畏缩不前,反而正面出击,提出了有效的解决方案。很多前端应用的开发框架,比如Backbone、EmberJ
    S等,都要求开发者继承此框架特有的一些JavaScript对象。这种方式有其长处,但它不必要地污染了开发者自己代码的对象空间,还要
    求开发者去了解内存里那些抽象对象。尽管如此我们还是接受了这种方式,因为网络最初的设计无法提供我们今天所需的交互性,于是我们需要框
    架,来帮我们填补JavaScript和HTML之间的鸿沟。而且有了它,你不用再“直接”操控DOM,只要给你的DOM注上metada
    ta(即AngularJS里的directive们),然后让AngularJS来帮你操纵DOM。同时,AngularJS不依赖(也
    不妨碍)任何其他的框架。你甚至可以基于其它的框架来开发AngularJS应用。API地址:http://docs.angularj
    s.org/api/http://docs.angularjs.org/api/;AngularJS在github上的中文粗译版地
    址:https://github.com/basestyle/angularjs-cnhttps://github.com/bas
    estyle/angularjs-cn。AngularJS四大特征AngularJS的设计思想与jquery完全不同,前者操作的是
    变量$scope$http,后者操作的DOM。MVC模式Angular遵循软件工程的M(数据)V(视图)C(控制器)模式,并鼓励
    展现,数据,和逻辑组件之间的松耦合.通过依赖注入(dependencyinjection),Angular为客户端的Web应用带
    来了传统服务端的服务,例如独立于视图的控制。因此,后端减少了许多负担,产生了更轻的Web应用。双向绑定AngularJS是建立在
    这样的信念上的:即声明式编程应该用于构建用户界面以及编写软件构建,而指令式编程非常适合来表示业务逻辑。框架采用并扩展了传统HTML
    ,通过双向的数据绑定来适应动态内容,双向的数据绑定允许模型和视图之间的自动同步。因此,AngularJS使得对DOM的操作不再重要
    并提升了可测试性。这里是区别于Jquery的,jq操作的是dom对象,angularJS操作的是变量。依赖注入依赖注入(Depen
    dencyInjection,简称DI)是一种设计模式,指某个对象依赖的其他对象无需手工创建,只需要“吼一嗓子”,则此对象在创
    建时,其依赖的对象由框架来自动创建并注入进来,其实就是最少知识法则;模块中所有的service和provider两类对象,都可以根
    据形参名称实现DI.控制器就是通过依赖注入的方式实现对服务的调用。模块化设计高内聚低耦合法则高内聚:每个模块的具体功能具体实现低耦
    合:模块之间尽可能的少用关联和依赖1)官方提供的模块??ng(最核心)、ngRoute(路由)、ngAnimate(动画)2)用户
    自定义的模块??angular.module(''模块名'',[])2.3什么时候该用AngularJSAngularJS是
    一个MV框架,最适于开发客户端的单页面应用。它不是个功能库,而是用来开发动态网页的框架。它专注于扩展HTML的功能,提供动态
    数据绑定(databinding),而且它能跟其它框架(如jQuery)合作融洽。如果你要开发的是单页应用,AngularJS就
    是你的上上之选。Gmail、GoogleDocs、Twitter和Facebook这样的应用,都很能发挥AngularJS的长处
    。但是像游戏开发之类对DOM进行大量操纵、又或者单纯需要极高运行速度的应用,就不是AngularJS的用武之地了。2.4下载A
    ngularJS并加入项目从angular提供的网址下载1.第一种方式是从angualr提供的网址上下载,如图,可以选择自己想要的
    版本。https://code.angularjs.org/https://code.angularjs.org/2.例如我想下
    载1.7.8的版本,我点入1.7.8的链接后,可以看到很多文件。如果我只是想下载angular.min.js的话,可以点击angu
    lar.min.js,点开后可以看到一堆js代码。3.随便找个文件夹,新建一个js文件,我将其命名为angular-1.7.8.m
    in.js(文件名随意),然后用notepad++打开,将网页上的js代码复制到angular-1.7.8.min.js中。4.复
    制完后保存文件。在同一目录下创建index.html文件,测试刚刚创建的angular-1.7.8.min.js是否能够使用。用能
    编辑文本文件的软件打开index.html,在index.html添加如图内容。?5.编辑完index.html后,保存文件,在浏
    览器打开index.html,结果如图。说明angular-1.7.8.min.js能正常使用。?从GitHub上下载第二种方式是
    直接从GitHub上下载整个项目的压缩包。Windows可选择zip包下载,linux可选择tar.gz包下载。https://g
    ithub.com/angular/angular.js/releaseshttps://github.com/angular/a
    ngular.js/releases从GitHub上下载还可以通过使用Eclipse来下载整个项目文件。打开Eclipse,选择F
    ile-->import。在弹出的窗口选择Git-->ProjectsfromGit。然后选择CloneURI。接下来将an
    gularjs在GitHub上的网址复制到URI中。选择next后,可以选择分支。接着填入一个空文件夹的路径以及选择初始的分支。最
    后选择导入的方式。如果导入的方式跟我一样是选择importasgeneralproject,完成导入后可以看到Project
    Explorer上显示整个工程项目。3AugularJS特性AngularJS是一个新出现的强大客户端技术,提供给大家的一种开
    发强大应用的方式。这种方式利用并且扩展HTML,CSS和javascript,并且弥补了它们的一些非常明显的不足。本应该使用HTM
    L来实现而现在由它开发的动态一些内容。AngularJS有五个最重要的功能和特性:3.1特性一:双向的数据绑定数据绑定可能是An
    gularJS最实用的特性。它能够帮助你避免书写大量的初始代码从而节约开发时间。一个典型的Web应用可能包含了80%的代码用来处理
    ,查询和监听DOM。数据绑定是的代码更少,你可以专注于你的应用。我们想象一下Model是你的应用中的简单事实。你的Model是你用
    来读取或者更新的部分。数据绑定指令提供了你的Model投射到view的方法。这些投射可以无缝的,毫不影响的应用到Web应用中。传统
    来说,当model变化了。开发人员需要手动处理DOM元素并且将属性反映到这些变化中。这个一个双向的过程。一方面,model变化驱
    动了DOM中元素变化,另一方面,DOM元素的变化也会影响到Model。这个在用户互动中更加复杂,因为开发人员需要处理和解析这些互动
    ,然后融合到一个model中,并且更新View。这是一个手动的复杂过程,当一个应用非常庞大的时候,将会是一件非常费劲的事情。这里肯
    定有更好的解决方案!那就是AngularJS的双向数据绑定,能够同步DOM和Model等等。这里有一个非常简单的例子,用来演示一个
    input输入框和

    元素的双向绑定(例01):<
    head>iv>placeholder="请输入名字">

    Hello,{{user.name}}!

    body>说明:实际效果请大家看AngularJS/demo/index.html还有一个例子,在AngularJS
    中创建控制器时,将$scope对象作为参数传递。在控制器中创建的属性可以在视图中引用:-controller="myCtrl">

    {{carname}}

    运行效果:向控制器中的$scop
    e对象添加属性时,视图(HTML)可以访问这些属性。在视图中,您不使用前缀$scope,只需引用属性名称,如{{carname}}
    。3.2特性二:模板在AngularJS中,一个模板就是一个HTML文件。但是HTML的内容扩展了,包含了很多帮助你映射mode
    l到view的内容。HTML模板将会被浏览器解析到DOM中。DOM然后成为AngularJS编译器的输入。AngularJS将会遍
    历DOM模板来生成一些指导,即,directive(指令)。所有的指令都负责针对view来设置数据绑定。我们要理解AuguarJS
    并不把模板当做String来操作。输入AngularJS的是DOM而非string。数据绑定是DOM变化,不是字符串的连接或者in
    nerHTML变化。使用DOM作为输入,而不是字符串,是AngularJS区别于其它的框架的最大原因。使用DOM允许你扩展指令词汇
    并且可以创建你自己的指令,甚至开发可重用的组件。最大的好处是为设计师和开发者创建了一个紧密的工作流。设计师可以像往常一样开发标签,
    然后开发者拿过来添加上功能,通过数据绑定将会使得这个过程非常简单。这里有一个例子,我们使用ng-repeat指令来循环图片数组并且
    加入img模板,如下:functionAlbumCtrl($scope){scope.images=[{"image
    ":"img/image_01.png","description":"Image01description"},{
    "image":"img/image_02.png","description":"Image02descriptio
    n"},{"image":"img/image_03.png","description":"Image03des
    cription"},{"image":"img/image_04.png","description":"Image
    04description"},{"image":"img/image_05.png","description":
    "Image05description"}];}

      }"alt="{{image.description}}">
    这里还有一件事值得提一句,Ang
    ularJS并不强制你学习一个新的语法或者从你的应用中提出你的模板。3.3特性三:MVC针对客户端应用开发AngularJS吸收
    了传统的MVC基本原则。MVC或者Model-View-Controll设计模式针对不同的人可能意味不同的东西。AngularJS
    并不执行传统意义上的MVC,更接近于MVVM(Moodel-View-ViewModel)。Modelmodel是应用中的简单数
    据。一般是简单的javascript对象。这里没有必要继承框架的classes,使用proxy对象封装或者使用特别的setter/
    getter方法来访问。事实上我们处理vanillajavascript的方法就是一个非常好的特性,这种方法使得我们更少使用应用
    的原型。ViewModelviewmodel是一个用来提供特别数据和方法从而维护指定view的对象。viewmodel是$scop
    e的对象,只存在于AnguarJS的应用中。$scope只是一个简单的js对象,这个对象使用简单的API来侦测和广播状态变化。Co
    ntrollercontroller负责设置初始状态和参数化$scope方法用以控制行为。需要指出的controller并不保存状
    态也不和远程服务互动。Viewview是AngularJS解析后渲染和绑定后生成的HTML。这个部分帮助你创建Web应用的架构。
    $scope拥有一个针对数据的参考,controller定义行为,view处理布局和互动。3.4特性四:服务和依赖注入Angul
    arJS服务其作用就是对外提供某个特定的功能。AngularJS拥有内建的依赖注入(DI)子系统,可以帮助开发人员更容易的开发,理
    解和测试应用。DI允许你请求你的依赖,而不是自己找寻它们。比如,我们需要一个东西,DI负责找创建并且提供给我们。为了而得到核心的A
    ngularJS服务,只需要添加一个简单服务作为参数,AngularJS会侦测并且提供给你:functionEditCtrl($
    scope,$location,$routeParams){//Somethingcleverhere...}你也可
    以定义自己的服务并且让它们注入:angular.module(''MyServiceModule'',[]).factory(''n
    otify'',[''$window'',function(win){returnfunction(msg){win.
    alert(msg);};}]);functionmyController(scope,notifyService){
    scope.callNotify=function(msg){notifyService(msg);};}myCont
    roller.$inject=[''$scope'',''notify''];3.5特性五:指令(Directives)指令是我个
    人最喜欢的特性。你是不是也希望浏览器可以做点儿有意思的事情?那么AngularJS可以做到。指令可以用来创建自定义的标签。它们可以
    用来装饰元素或者操作DOM属性。可以作为标签、属性、注释和类名使用。这里是一个例子,它监听一个事件并且针对的更新它的$scope
    ,如下:myModule.directive(''myComponent'',function(mySharedService)
    {return{restrict:''E'',controller:function($scope,$attrs,m
    ySharedService){$scope.$on(''handleBroadcast'',function(){$sc
    ope.message=''Directive:''+mySharedService.message;});},rep
    lace:true,template:''''};});然后,你可以使用这个自定义的directive来使用:<
    my-componentng-model="message">使用一系列的组件来创建你自己的应用将
    会让你更方便的添加,删除和更新功能。4功能介绍4.1数据绑定AngularJS的双向数据绑定,意味着你可以在Mode(JS)中
    改变数据,而这些变动立刻就会自动出现在View上,反之亦然。即:一方面可以做到model变化驱动了DOM中元素变化,另一方面也可以
    做到DOM元素的变化也会影响到Model。在我们使用jQuery的时候,代码中会大量充斥类似这样的语句:varval=$(‘
    #id’).val();$(‘#id’).html(str);等等,即频繁的DOM操作(读取和写入),其实我们的最终目的并不是要
    操作DOM,而是要实现业务逻辑。ng的绑定将让你摆脱DOM操作,只要模板与数据通过声明进行了绑定,两者将随时保持同步,最新的数据会
    实时显示在页面中,页面中用户修改的数据也会实时被记录在数据模型中。从View到Controller再到View的数据交互(例01)
    ……placeholder="请输入名称"/>Hello,{{user.name}}!……关键:ng-app、ng-mo
    del和{{user.name}}首先:元素的ng-app属性。标识这个DOM里面的内容将启用Angul
    arJS应用。其次:告诉AngularJS,对页面上的“user.name”这个Model进行双向数据绑定。第三:告诉Angul
    arJS,在“{{user.name}}”这个指令模版上显示“user.name”这个Model的数据。从Server到Cont
    roller再到View的数据交互(例02):……er="demoController">led="disabled"/>er()">AJAX获取名字……demoApp.controller("demoController",function
    ($http,$scope){$scope.getAjaxUser=function(){//$http.get
    ({url:"../xxx.action"}).success(function(data){//$scope.user=da
    ta;//});$scope.user={"name":"从JOSN中获取的名称","age":22};};}
    );改变$scope中的user,View也会自动更新。4.2scopes、module、controller4.2.1sc
    opes$scope是一个把view(一个DOM元素)连结到controller上的对象。在我们的MVC结构里,这个$scope
    将成为model,它提供一个绑定到DOM元素(以及其子元素)上的excecutioncontext。尽管听起来有点复杂,但$
    scope实际上就是一个JavaScript对象,controller和view都可以访问它,所以我们可以利用它在两者间传递信息
    。在这个$scope对象里,我们既存储数据,又存储将要运行在view上的函数。每一个Angular应用都会有一个$rootS
    cope。这个$rootScope是最顶级的scope,它对应着含有ng-app指令属性的那个DOM元素。app.run(
    function($rootScope){$rootScope.name="张三";});如果页面上没有明确设定$sc
    ope,Angular就会把数据和函数都绑定到这里,第一部分中的例子就是靠这一点成功运行的。这样,我们就可以在view的任何
    地方访问这个name属性,使用模版表达式{{}},像这样:{{name}}4.2.2module首先需要明确一下模板的概
    念。在我还不知道有模板这个东西的时候,曾经用js拼接出很长的HTML字符串,然后append到页面中,这种方式想想真是又土又笨。后
    来又看到可以把HTML代码包裹在一个将当前元素的内容添加到有ng-transc
    lude属性的这个DIV下,默认是隐藏的。3.Controller信息:demoApp.controller("test7Cont
    roller",function($scope){$scope.title=''个人简介'';$scope.text=''大家
    好,我正在研究AngularJs,欢迎大家与我交流。'';$scope.updateInfo=function(){$scop
    e.title=''个人信息'';$scope.text=''大家好,今天天气真好!'';}});4.指令使用方式(View信息)
    为:{{text}}Etitle指向Control
    ler中的$scope.title。注意命名方式:指令名为userInfo,对应的标签为user-info。4.7服务(serv
    ice)4.7.1服务介绍服务这个概念其实并不陌生,在其他语言中如java便有这样的概念,其作用就是对外提供某个特定的功能,如
    消息服务,文件压缩服务等,是一个独立的模块。ng的服务是这样定义的:Angularservicesaresingletons
    objectsorfunctionsthatcarryoutspecifictaskscommontoweb
    apps.它是一个单例对象或函数,对外提供特定的功能。首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例。其次
    这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的。ng的避
    免全局变量污染意识非常强。ng提供了很多内置的服务,可以到API中查看http://docs.angularjs.org/api
    /。知道了概念,我们来拉一个service出来溜溜,看看到底是个什么用法。我们在controller中直接声明$locati
    on服务,这依靠ng的依赖注入机制。$location提供地址栏相关的服务,我们在此只是简单的获取当前的地址。服务的使用是如此简
    单,我们可以把服务注入到controller、指令或者是其他服务中。4.7.2自定义服务如同指令一样,系统内置的服务以$开头,
    我们也可以自己定义一个服务。定义服务的方式有如下几种:使用系统内置的$provide服务;使用Module的factory方法;使
    用Module的service方法。下面通过一个小例子来分别试验一下。我们定义一个名为remoteData服务,它可以从远程获取
    数据,这也是我们在程序中经常使用的功能。不过我这里没有远程服务器,就写死一点数据模拟一下。//使用$provide来定义vara
    pp=angular.module(''MyApp'',[],function($provide){$provide.f
    actory(''remoteData'',function(){vardata={name:''n'',value:
    ''v''};returndata;});});//使用factory方法app.factory(''remoteData'',
    function(){vardata={name:''n'',value:''v''};returndata;
    });//使用service方法app.service(''remoteData'',function(){this.name
    =''n'';this.value=''v'';});Module的factory和$provide的factory方法是一模一
    样的,从官网文档看它们其实就是一回事。至于Module内部是如何调用的,我此处并不打算深究,我只要知道怎么用就好了。再看Modul
    e的service方法,它没有return任何东西,是因为service方法本身返回一个构造器,系统会自动使用new关键字来创建出
    一个对象。所以我们看到在构造器函数内可以使用this,这样调用该服务的地方便可以直接通过remoteData.name来访问数据了
    。4.7.3管理服务的依赖关系服务与服务中间可以有依赖关系,例如我们这里定义一个名为validate的服务,它的作用是验证数据
    是否合法,它需要依赖我们从远程获取数据的服务remoteData。代码如下:在factory的参数中,我们可以直接传入服务r
    emoteData,ng的依赖注入机制便帮我们做好了其他工作。不过一定要保证这个参数的名称与服务名称一致,ng是根据名称来识别的。
    若参数的名次与服务名称不一致,你就必须显示的声明一下,方式如下:app.factory(''validate'',[''remoteD
    ata'',function(remoteDataService){returnfunction(){if(rem
    oteDataService.name==''n''){alert(''验证通过'');}};}]);我们在controlle
    r中注入服务也是同样的道理,使用的名称需要与服务名称一致才可以正确注入。否则,你必须使用$inject来手动指定注入的服务。比如:
    functiontestC(scope,rd){scope.getData=function(){alert(''
    name:''+rd.name+''value:''+rd.value);}}testC.$inject=[''$
    scope'',''remoteData''];在controller中注入服务,也可以在定义controller时使用数组作为第二
    个参数,在此处把服务注入进去,这样在函数体中使用不一致的服务名称也是可以的,不过要确保注入的顺序是一致的,如:app.contro
    ller(''testC'',[''$scope'',''remoteData'',function($scope,rd){$s
    cope.getData=function(){alert(''name:''+rd.name+''value:
    ''+rd.value);}}]);4.7.4自定义服务示例接下来让我们看下例子(例08自定义服务)代码,自定义userS
    ervice服务:demoApp.factory(''userService'',[''$http'',function($http
    ){vardoGetUser=function(userId,path){//return$http({//
    method:''JSONP'',//url:path//});/手动指定数据/vardata={userId
    :"woshishui",userName:"我是谁",userInfo:"我是谁!我是谁!"};;if(user
    Id==''zhangsan''){data={userId:"zhangsan",userName:"张三",
    userInfo:"我是张三,我为自己"};}elseif(userId==''lisi''){data={
    userId:"lisi",userName:"李四",userInfo:"我是李四,我为卿狂!"};}retur
    ndata;}return{/userService对外暴露的函数,可有多个/getUser:function
    (userId){returndoGetUser(userId,''../xxx/xxx.action'');}};}])
    ;我们创建了一个只有一个方法的userService,getUser为这个服务从后台获取用户信息的函数,并且对外暴露。当然,由于这
    是一个静态的例子,无法访问后台,那么我们便制定其返回的数据。然后我们把这个服务添加到我们的controller中。我们建立一个co
    ntroller并加载(或者注入)userService作为运行时依赖,我们把service的名字作为参数传递给controlle
    r函数:demoApp.controller("test8Controller",function($scope,user
    Service){/文章信息/$scope.articles=[{title:"爱飞像风",userId:"
    zhangsan",userName:"张三"},{title:"无法停止的雨",userId:"lisi",u
    serName:"李四"}];$scope.showUserInfo=false;//显示作者详细信息开关$scope
    .currentUser={};//当前选中的作者$scope.getUserInfo=function(userI
    d){$scope.currentUser=userService.getUser(userId);//调用userS
    ervice的getUser函数$scope.showUserInfo=true;setTimeout(function
    (){//定时器:隐藏作者详细信息$scope.showUserInfo=false;},3000);}});我们的
    userService注入到我们的test8Controller后,我们就可以像使用其他服务(我们前面提到的$http服务)一样的
    使用userService了。相关的HTML代码如下:/ViewHTML/inarticles">

    {{article_.title}}oid(0);"ng-click="getUserInfo(article_.userId)">{{article_.user
    Name}}
    献花(0)
    +1
    (本文系zymITsky首藏)