AngularJS核心语法进阶:从指令到模块化的完整指南
本文深入解析AngularJS核心语法,涵盖ng-src与ng-href指令防止资源加载错误的机制。详细阐述表达式与JavaScript eval的区别,以及利用$watch监控数据模型变化的原理与性能优化策略。此外,还介绍了如何通过module、factory和service组织代码,并演示了自定义Filter与引入第三方模块的最佳实践。
AngularJS指令与表达式基础
src和href属性
在AngularJS应用中,直接绑定src或href属性可能导致浏览器在Angular编译完成前尝试加载资源,从而引发错误。因此,应使用ng-src和ng-href指令。例如:
代码如下:
<img ng-src="/images/cats/{{favoriteCat}}">
<a ng-href="/shop/category={{number}}">Some text</a>
表达式
Angular模板支持数学运算、比较、布尔运算、位运算以及数组和对象的属性访问。与JavaScript的eval()不同,Angular使用自定义解释器执行表达式,因此功能受限。尽管限制较多,但Angular表达式对null和undefined具有更好的容错性:遇到错误时,模板仅不显示内容,而不会抛出NullPointerException。例如:
代码如下:
<div ng-controller='SomeController'>
<div>{{computer() /10 }}</div> //虽然是合法的,但是它把业务逻辑放到模板中了,应避免这种做法
</div>
控制器与DOM绑定机制
区分UI和控制器的职责
控制器绑定在特定的DOM片段上,负责管理该区域的内容。将控制器与DOM关联主要有两种方式:一是在模板中使用ng-controller声明;二是通过路由将控制器绑定到动态加载的视图(DOM片段)。支持嵌套控制器,它们通过原型继承共享数据模型和函数。实际的嵌套发生在$scope对象上,父控制器的$scope(包括所有属性和函数)会被传递给内部嵌套的$scope。例如:
代码如下:
<div ng-controller="ParentController">
<div ng-controller="ChildController">...</div>
</div>
$scope数据模型与监控策略
利用$scope暴漏模型数据
可以显式创建$scope属性,例如$scope.count = 5。也可以通过模板间接创建数据模型。
通过表达式。例如
代码如下:
<button ng-click='count=3'>Set count to three</button>
在表单项上使用ng-model
与表达式类似,ng-model指定的模型参数同样在外层控制器内生效。不同之处在于,ng-model在表单项与指定模型之间建立了双向绑定关系。
使用watch监控数据模型的变化
$watch函数的签名为:$watch(watchFn,watchAction,deepWatch)。watchFn是返回被监控数据模型当前值的Angular表达式或函数字符串;watchAction是watchFn变化时调用的函数或表达式,签名为function(newValue,oldValue,scope)。若deepWatch设为true,Angular将检查被监控对象的每个属性是否变化,适用于监控数组元素或对象所有属性,而非简单值。注意,监控大型集合时遍历运算复杂度较高。
$watch函数返回一个注销函数,当不再需要接收变更通知时,可调用该函数注销监控器。若需监控属性后注销,代码如下:var dereg = $scope.$watch('someModel.someProperty',callbackOnChange());... dereg();
实例代码如下:
代码如下:
<html ng-app>
<head>
<title>Your Shopping Cart</title>
<script type="text/javascript">
function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title:'Paint pots',quantity:8,price:3.95},
{title:'Polka dots',quantity:17,price:12.95},
{title:'Pebbles',quantity:5,price:6.95}
];
$scope.totalCart = function() {
var total = 0;
for (var i=0,len=$scope.items.length;i<len;i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
return total;
}
$scope.subtotal = function() {
return $scope.totalCart() - $scope.bill.discount;
}
function calculateDiscount(newValue,oldValue,scope) {
$scope.bill.discount = newValue > 100 ? 10 : 0;
};//这里的watchAction函数
$scope.$watch($scope.totalCart,calculateDiscount);//这里的watch函数
}
</script>
</head>
<body>
<div ng-controller="CartController">
<div ng-repeat='item in items'>
<span>{{item.title}}</span>
<input ng-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>
<script type="text/javascript" src="angular.min.js"></script>
</body>
</html>
上述watch存在性能问题,calculateTotals函数执行了6次,其中三次是因为循环,每次循环都会重新渲染数据。下面是改良后的代码
代码如下:
<html ng-app>
<head>
<title>Your Shopping Cart</title>
<script type="text/javascript">
function CartController($scope) {
$scope.bill = {};
$scope.items = [
{title:'Paint pots',quantity:8,price:3.95},
{title:'Polka dots',quantity:17,price:12.95},
{title:'Pebbles',quantity:5,price:6.95}
];
var totalCart = function() {
var total = 0;
for (var i=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>
<div ng-controller="CartController">
<div ng-repeat='item in items'>
<span>{{item.title}}</span>
<input ng-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>
<script type="text/javascript" src="angular.min.js"></script>
</body>
</html>
对于大型的itms数组来说,如果每次在Angular显示页面时只重新计算bill属性,那么性能会好很多。通过创建一个带有watchFn的$watch函数,我们可以实现这一点。
代码如下:
$scope.$watch(
var totalCart = function() {
var total = 0;
for (var i=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,Object OR constructor()) 说明: 一个可配置的服务,创建逻辑比较的复杂。如果你传递了一个Object作为参数,那么这个Object对象必须带有一个名为$get的函数,这个函数需要返回服务的名称。否则,angularjs会认为你传递的时一个构造函数,调用构造函数会返回服务实例对象。 factory(name,$get Function()) 说明:一个不可配置的服务,创建逻辑比较的复杂。你需要指定一个函数,当调用这个函数时,会返回服务实例。可以看成provider(name,{$get:$getFunction()})的形式。 service(name,constructor()) 一个不可配置的服务,创建逻辑比较的简单。与上面的provider函数的constructor参数类似,Angular调用他可以创建服务实例。
使用module factory的例子
代码如下:
<html ng-app='ShoppingModule'>
<head>
<title>Your Shopping Cart</title>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript">
var ShoppingModule = angular.module('ShoppingModule',[]);
ShoppingModule.factory('Items',function() {
var items = {};
items.query = function() {
return [
{title:'Paint pots',description:'Pots full of Paint',price:3.95},
{title:'Paint pots',description:'Pots full of Paint',price:3.95},
{title:'Paint pots',description:'Pots full of Paint',price:3.95}
];
};
return items;
});
function ShoppingController($scope,Items) {
$scope.items = Items.query();
}
</script>
</head>
<body ng-controller='ShoppingController'>
<h1>Shop!!</h1>
<table>
<tr ng-repeat='item in items'>
<td>{{item.title}}</td>
<td>{{item.description}}</td>
<td>{{item.price | currency}}</td>
</tr>
</table>
</body>
</html>
引入第三方模块
在大多数应用中,创建供所有代码使用的单个模块,并把所有依赖的东西放入这个模块中,这样就会工作的很好。但是,如果你打算使用第三方包提供的服务或者指令,他们一般都带有自己的模块,你需要在应用模块中定义依赖关心才能引用他们。 例如: var appMod = angular.module('app',['Snazzy','Super']);
关于filter的例子
代码如下:
<html ng-app='ShoppingModule'>
<head>
<title>Your Shopping Cart</title>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript">
var ShoppingModule = angular.module('ShoppingModule',[]);
ShoppingModule.filter('titleCase',function() {
var titleCaseFilter = function(input) {
var words = input.split(' ');
for(var i=0;i<words.length;i++) {
words[i] = words[0].charAt(0).toUpperCase() + words[i].slice(1);
}
return words.join(' ');
};
return titleCaseFilter;
});
function ShoppingController($scope) {
$scope.pageHeading = 'this is a test case';
}
</script>
</head>
<body ng-controller='ShoppingController'>
<h1>{{pageHeading | titleCase}}</h1>
</body>
</html>为什么叫JavaScript:语言命名由来及与Java的历史渊源
2026-11-05 11:37:48
JS获取当前时间年月日时分秒并格式化为YYYY-MM-DD HH:mm:ss
2026-11-04 21:25:21
不同语言间JS与Java/Python加密结果不一致怎么办
2026-11-04 18:27:23
Mock.js 拦截 Axios 和 Fetch 请求的原理与配置方法
2026-11-04 08:13:59
JS怎么获取当前日期并格式化为YYYY-MM-DD
2026-11-03 18:47:03
