AngularJS语法详解(续)
src和href属性
Angularjs中src应写成ng-src,href应写成ng-href例如:
<imgng-src="/images/cats/{{favoriteCat}}"> <ang-href="/shop/category={{number}}">Sometext</a>
表达式
在模板中可以进行简单的数学运算、比较运算、布尔运算、位运算、引用数组、和对象符号等尽管我们可以使用表达式做很多事情,但是表达式是使用一个自定义的解释器来执行的(Angular的一部分),而不是用Javascript得eval()函数执行的,所以局限性较大。
虽然很多方面这里的表达式比Javascript更严格,但是他们对undefined和null的容错性更好,如果遇到错误,模板只是简单的什么都不显示,而不会抛出一个NullPointerException错误。例如:
<divng-controller='SomeController'> <div>{{computer()/10}}</div>//虽然是合法的,但是它把业务逻辑放到模板中了,应避免这种做法 </div>
区分UI和控制器的职责
控制器是绑定在特定DOM片段上的,这些片段就是他们需要负责管理的内容。有两种主要的方法可以把控制器关联到DOM节点上,一种在模板中通过ng-controller声明,第二种是通过路由把它绑定到一个动态加载的DOM模板片段上,这个模板叫视图。我们可以创建嵌套的控制器,他们可以通过继承数结构来共享数据模型和函数,真实的嵌套发生在$scope对象上,通过内部的原始继承机制,父控制器对象的$scope会被传递到内部嵌套的$scope(所有属性,包括函数)。例如:
<divng-controller="ParentController"> <divng-controller="ChildController">...</div> </div>
利用$scope暴漏模型数据
可以显示创建$scope属性,例如$scope.count=5。还可以间接的通过模板自身创建数据模型。
通过表达式。例如
<buttonng-click='count=3'>Setcounttothree</button>
在表单项上使用ng-model
与表达式类似,ng-model上指定的模型参数同样工作在外层控制器内。唯一的不同点在于,这样会在表单项和指定的模型之间建立双向绑定关系。
使用watch监控数据模型的变化
$watch的函数签名是:$watch(watchFn,watchAction,deepWatch)
watchFn是一个带有Angular表达式或者函数的字符串,它会返回被监控的数据模型的当前值。watchAction是一个函数或者表达式,当watchFn发生变化时调用。其函数签名为:
function(newValue,oldValue,scope)deepWatch如果设置为true,这个可选的布尔值参数将会命令Angular去检查被监控对象的每一个属性是否发生了变化。如果你向监控数组中的元素,或者对象上的所有属性,而不是值监控一个简单的值,你就可以使用这个参数。注意,Angular需要遍历数组或者对象,如果集合比较大,那么运算复杂呢就会比较的重。
$watch函数会返回一个函数,当你不需要接收变更通知时,可以用这个返回的函数注销监控器。
如果我们需要监控一个属性,然后接着注销监控,我们就可以使用以下的代码:vardereg=$scope.$watch('someModel.someProperty',callbackOnChange());
...dereg();
实例代码如下:
<htmlng-app> <head> <title>YourShoppingCart</title> <scripttype="text/javascript"> functionCartController($scope){ $scope.bill={}; $scope.items=[ {title:'Paintpots',quantity:8,price:3.95}, {title:'Polkadots',quantity:17,price:12.95}, {title:'Pebbles',quantity:5,price:6.95} ]; $scope.totalCart=function(){ vartotal=0; for(vari=0,len=$scope.items.length;i<len;i++){ total=total+$scope.items[i].price*$scope.items[i].quantity; } returntotal; } $scope.subtotal=function(){ return$scope.totalCart()-$scope.bill.discount; } functioncalculateDiscount(newValue,oldValue,scope){ $scope.bill.discount=newValue>100?10:0; }//这里的watchAction函数 $scope.$watch($scope.totalCart,calculateDiscount);//这里的watch函数 } </script> </head> <body> <divng-controller="CartController"> <divng-repeat='iteminitems'> <span>{{item.title}}</span> <inputng-model='item.quantity'> <span>{{item.price|currency}}</span> <span>{{item.price*item.quantity|currency}}</span> </div> <div>Total:{{totalCart()|currency}}</div> <div>Discount:{{bill.discount|currency}}</div> <div>SubTotal:{{subtotal()|currency}}</div> </div> <scripttype="text/javascript"src="angular.min.js"></script> </body> </html>
上面的watch存在性能问题,calculateTotals函数执行了6次,其中三次是因为循坏,每次循环,都会重新渲染数据。
下面是改良后的代码
<htmlng-app> <head> <title>YourShoppingCart</title> <scripttype="text/javascript"> functionCartController($scope){ $scope.bill={}; $scope.items=[ {title:'Paintpots',quantity:8,price:3.95}, {title:'Polkadots',quantity:17,price:12.95}, {title:'Pebbles',quantity:5,price:6.95} ]; vartotalCart=function(){ vartotal=0; for(vari=0,len=$scope.items.length;i<len;i++){ total=total+$scope.items[i].price*$scope.items[i].quantity; } $scope.bill.totalcart=total; $scope.bill.discount=total>100?10:0; $scope.bill.subtotal=total-$scope.bill.discount; } $scope.$watch('items',totalCart,true);//只用watch着items的变化 } </script> </head> <body> <divng-controller="CartController"> <divng-repeat='iteminitems'> <span>{{item.title}}</span> <inputng-model='item.quantity'> <span>{{item.price|currency}}</span> <span>{{item.price*item.quantity|currency}}</span> </div> <div>Total:{{bill.totalcart|currency}}</div> <div>Discount:{{bill.discount|currency}}</div> <div>SubTotal:{{bill.subtotal|currency}}</div> </div> <scripttype="text/javascript"src="angular.min.js"></script> </body> </html>
对于大型的itms数组来说,如果每次在Angular显示页面时只重新计算bill属性,那么性能会好很多。通过创建一个带有watchFn的$watch函数,我们可以实现这一点。
$scope.$watch( vartotalCart=function(){ vartotal=0; for(vari=0,len=$scope.items.length;i<len;i++){ total=total+$scope.items[i].price*$scope.items[i].quantity; } $scope.bill.totalcart=total; $scope.bill.discount=total>100?10:0; $scope.bill.subtotal=total-$scope.bill.discount; });
监控多个东西
如果你想监控多个属性或者对象,并且当其中任何一个发生变化时就会去执行一个函数,你有两种基本的选择:
监控把这些属性连接起来之后的值
把他们放在一个数组或者对象中,然后给deepWAtch参数传递一个值
分别说明:
在第一种情况下,如果你的作用域中存在一个things对象,它带有两个属性a和b,当这两个属性发生变化时都需要执行callMe()函数,你可以同时监控这两个属性$scope.$watch('things.a+things.b',callMe(...));
当列表非常长,你就需要写一个函数来返回连接之后的值。
在第二种情况下,需要监控things对象的所有属性,你可以这么做:
$scope.$watch('things',callMe(...),true);
使用module组织依赖关系
provider(name,ObjectORconstructor())说明:一个可配置的服务,创建逻辑比较的复杂。如果你传递了一个Object作为参数,那么这个Object对象必须带有一个名为$get的函数,这个函数需要返回服务的名称。否则,angularjs会认为你传递的时一个构造函数,调用构造函数会返回服务实例对象。
factory(name,$getFunction())说明:一个不可配置的服务,创建逻辑比较的复杂。你需要指定一个函数,当调用这个函数时,会返回服务实例。可以看成provider(name,{$get:$getFunction()})的形式。
service(name,constructor())一个不可配置的服务,创建逻辑比较的简单。与上面的provider函数的constructor参数类似,Angular调用他可以创建服务实例。
使用modulefactory的例子
<htmlng-app='ShoppingModule'> <head> <title>YourShoppingCart</title> <scripttype="text/javascript"src="angular.min.js"></script> <scripttype="text/javascript"> varShoppingModule=angular.module('ShoppingModule',[]); ShoppingModule.factory('Items',function(){ var items={}; items.query=function(){ return[ {title:'Paintpots',description:'PotsfullofPaint',price:3.95}, {title:'Paintpots',description:'PotsfullofPaint',price:3.95}, {title:'Paintpots',description:'PotsfullofPaint',price:3.95} ]; }; returnitems; }); functionShoppingController($scope,Items){ $scope.items=Items.query(); } </script> </head> <bodyng-controller='ShoppingController'> <h1>Shop!!</h1> <table> <trng-repeat='iteminitems'> <td>{{item.title}}</td> <td>{{item.description}}</td> <td>{{item.price|currency}}</td> </tr> </table> </body> </html>
引入第三方模块
在大多数应用中,创建供所有代码使用的单个模块,并把所有依赖的东西放入这个模块中,这样就会工作的很好。但是,如果你打算使用第三方包提供的服务或者指令,他们一般都带有自己的模块,你需要在应用模块中定义依赖关心才能引用他们。例如:
varappMod=angular.module('app',['Snazzy','Super']);
关于filter的例子
<htmlng-app='ShoppingModule'> <head> <title>YourShoppingCart</title> <scripttype="text/javascript"src="angular.min.js"></script> <scripttype="text/javascript"> varShoppingModule=angular.module('ShoppingModule',[]); ShoppingModule.filter('titleCase',function(){ vartitleCaseFilter=function(input){ varwords=input.split(''); for(vari=0;i<words.length;i++){ words[i]=words[0].charAt(0).toUpperCase()+words[i].slice(1); } returnwords.join(''); }; returntitleCaseFilter; }); functionShoppingController($scope){ $scope.pageHeading='thisisatestcase'; } </script> </head> <bodyng-controller='ShoppingController'> <h1>{{pageHeading|titleCase}}</h1> </body> </html>