angular学习(八)—— 表达式


angular表达式是一些类似javascript的代码片段,主要用在插补绑定,例如<span title="{ { attrBinding } }">{ { textBinding } }</span>,或者直接用于angular指令的属性值,例如ng-click="functionExpression()"

angular表达式

有效表达式示例如下:

  • 1+2
  • a+b
  • user.name
  • items[index]

angular表达式 VS javascript表达式

angular表达式和javascript表达式很相似,但也有些不同之处,如下:

  • Context:javascript表达式的Context可以是整个window,而angular的Context只是一个scope对象范围内。
  • Forgiving:javascript中,如果对undefined的属性求值会产生ReferenceError和TypeError错误,而在angular中,表达式的求值会对undefined和null非常宽容。
  • Filters:可以在展示数据之前先用filters对表达式进行数据格式化。
  • 不能写控制流语句:不能在angular表达式写控制流语句:条件,循环,异常。
  • 不能做函数定义:不能在angular表达式定义函数,即使在ng-init指令中。
  • 不能用在正则表达式:不能在angular表达式创建正则表达式。
  • 不能用new创建对象:不能在angular表达式用new操作符创建对象。
  • 不能使用位,逗号,void操作符:不能再angular表达式使用位,逗号,void操作符。

示例

这个示例可以计算不同的angular表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html>
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<script>
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
</script>
<body ng-app="expressionExample">
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
</body>
</html>

Context

angular不能用javascript的eval()方法对表达式进行求值,而且用angular的$parse服务来处理这些表达式。
angular表达式不能直接访问全局变量window,document,location。这种限制是angular有意这样规定的,防止访问常见bug的来源。
你可以在控制器的函数中去使用$window$location服务,然后从表达式中调用,这些服务也可以访问全局变量。
可以用this标识符访问上下文对象,也可以用$locals标识符访问locals的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<script>
angular.module('expressionExample', [])
.controller('ExampleController', ['$window', '$scope', function($window, $scope) {
$scope.name = 'World';
$scope.greet = function() {
$window.alert('Hello ' + $scope.name);
};
}]);
</script>
<body ng-app="expressionExample">
<div class="example2" ng-controller="ExampleController">
Name: <input ng-model="name" type="text"/>
<button ng-click="greet()">Greet</button>
<button ng-click="alert('Should not see me')">Won't greet</button>
</div>
</body>
</html>

Forgiving

表达式求值对undefined和null是宽容的。在javascript中对a.b.c求值时,如果a不是一个对象,就会抛异常。作为一个通用语言,表达式求值主要用于数据绑定,通常是这样:

1
{{a.b.c}}

a如果是undefined(可能我们在等待服务器的返回,它马上就会变成defined),这时候什么也不显示通常要比抛出一个异常明智的多。如果表达式求值不能宽容,那么我们需要写杂乱的绑定代码,如下:

1
{{((a||{}).b||{}).c}}

同样,在undefined或者null的情况下,调用a的函数a.b.c()也只是简单的返回undefined

不能写控制流语句

除了三元运算符,你不能在angular表达式写一个控制流语句。这和angular的核心思想有关:应用的逻辑应该在控制器中,而不是视图中。如果你确实需要在angular表达式写一个条件,循环,或者异常,委托给javascript方法。

不能做函数定义和正则表达式

你不能在angular表达式定义函数或者创建正则表达式,这样可以避免模板中有复杂的模型转换逻辑。这些逻辑可以放在控制器中或者一个专门的过滤器中,控制器和过滤器还方面进行测试。

$event

ngClickngFocus这样的指令会讲\$event对象暴露给表达式的scope,这个对象是个jQuery事件对象或者说一个jqLite对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html>
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<script>
angular.module('eventExampleApp', []).
controller('EventController', ['$scope', function($scope) {
/*
* 暴露事件对象给scope
*/
$scope.clickMe = function(clickEvent) {
$scope.clickEvent = simpleKeys(clickEvent);
console.log(clickEvent);
};
/*
* 返回一个非对象键的对象拷贝
* 防止循环引用
*/
function simpleKeys(original) {
return Object.keys(original).reduce(function(obj, key) {
obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
return obj;
}, {});
}
}]);
</script>
<body ng-app="eventExampleApp">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Event</button>
<p><code>$event</code>: <pre> {{$event | json}}</pre></p>
<p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p>
</div>
</body>
</html>

注意在上面的例子中,我们可以传递$eventclickMe,但是不能显示{ {$event} },因为$event超出了绑定的scope。

一次性绑定

一次性表达式以::开头,当它们稳定后就会停止重新计算,这里说的稳定是指第一次digest之后表达式的值不是undefined的情况下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<script>
angular.module('oneTimeBidingExampleApp', []).
controller('EventController', ['$scope', function($scope) {
var counter = 0;
var names = ['Igor', 'Misko', 'Chirayu', 'Lucas'];
/*
* expose the event object to the scope
*/
$scope.clickMe = function(clickEvent) {
$scope.name = names[counter % names.length];
counter++;
};
}]);
</script>
<body ng-app="oneTimeBidingExampleApp">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Click Me</button>
<p id="one-time-binding-example">One time binding: {{::name}}</p>
<p id="normal-binding-example">Normal binding: {{name}}</p>
</div>
</body>
</html>

使用一次性绑定的原因

一次性绑定的主要目的是,提供了创建绑定的方法,一旦绑定被稳定化,相应的资源就可以被注销和释放。减少需要观察的表达式数量,使得digest循环更快速,在相同时间内更多的信息可以展示出来。

价值稳定算法

只要该值不是undefined,一次性绑定表达式只会保留digest结束时表达式的值。如果该值在digest循环的时候设置,稍后在相同的循环中它被设置成undefined,那么表达式不满足稳定的条件,它会被继续监视。

  1. 给定一个以::开头的表达式,当digest进入后,expression被进行脏检查,存储表达式的值为V。
  2. 如果V不是undefined,把表达式标记为稳定的,并且分配一个任务,这个任务在退出digest循环时,注销该表达式的watch。
  3. 继续处理digest循环
  4. 当digest完成之后并且所有的值都已经解决。开始运行watch注销任务,检查一下表达式的值是否稳定的。如果是,那么注销掉watch,如果不是,保留这个watch在digest循环中,开始步骤1的循环。

怎样用好一次性绑定的优势

如果表达式在设置后不会更改,则是一次性绑定的最佳选择。这里有三个例子:

当插补文本或者属性:

1
<div name="attr: {{::color}}">text: {{::name | uppercase}}</div>

当我们使用双向绑定指令而参数不会更改时:

1
2
3
4
5
6
7
8
9
someModule.directive('someDirective', function() {
return {
scope: {
name: '=',
color: '@'
},
template: '{{name}}: {{color}}'
};
});
1
<div some-directive name="::myName" color="My color is {{::myColor}}"></div>

当我使用有一串表达式的指令时:

1
2
3
<ul>
<li ng-repeat="item in ::items | orderBy:'name'">{{item.name}};</li>
</ul>
文章目录
  1. 1. angular表达式
  2. 2. angular表达式 VS javascript表达式
  3. 3. 示例
  4. 4. Context
  5. 5. Forgiving
  6. 6. 不能写控制流语句
  7. 7. 不能做函数定义和正则表达式
  8. 8. $event
  9. 9. 一次性绑定
    1. 9.1. 使用一次性绑定的原因
    2. 9.2. 价值稳定算法
    3. 9.3. 怎样用好一次性绑定的优势
|