表达式(Expressions)是与javascript相似的代码片断。虽然相似,但并不完全与javascript相同,也不是由javascript引擎直接执行的。所以某些javascript语法并不能直接在angularjs的表达式中使用。
表达式通常放在binding中,比如{{expression}}
。
表达式由$parse
服务处理。
下面是几种合法的angularjs表达式:
把angularjs的表达式当成javascript表达式是一件很诱人的事情,可惜这不完全正确,因为它不是用Javascript的eval()
来执行的。
它们之间有以下几点区别:
属性求值 Attribute Evaluation: 所有的属性是从scope中计算,而不像Javascript那样,从全局的window
对象中计算
空值友好 Forgiving: 如果表达式中遇到了undefined
或null
,会跳过它们处理其它的,不会抛出NullPointerExceptions
.
没有控制语句 No Control Flow Statements: 没有条件判断语句、循环、抛异常等
过滤器 Filters: 可以把表达式的计算结果传给filters继续处理。比如,你可以把一个date对象交给filter处理成易读的本地格式。
另外,如果你想运行Javascript代码,你必须在controller中新建一个方法,然后在angularjs表达式中调用它。如果你想在Javascript中执行angularjs表达式,可使用由angularjs提供的$eval()
方法。
index.html
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
</head>
<body>
1+2={{1+2}}
</body>
</html>
End to end test
it('should calculate expression in binding', function() {
expect(binding('1+2')).toEqual('3');
});
jsfiddle link?
你可以在下面的示例中尝试不同的表达式(需要到相应的jsfiddle页面上):
index.html
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="Cntl2" 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">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
</body>
</html>
script.js
function Cntl2($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);
};
}
End to end test
it('should allow user expression testing', function() {
element('.expressions :button').click();
var li = using('.expressions ul').repeater('li');
expect(li.count()).toBe(1);
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
});
jsfiddle link
属性的求值发生在scope
上。在Javascript中,属性名默认是在全局的window
对象上,在angular表达式中,使用$window
对象来引用全局window
对象。比如,你想调用alert()
,在angularjs中就必须使用$window.alert()
。这么做是为了防止我们的代码意外访问了全局状态,容易引起bug。
index.html
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div class="example2" ng-controller="Cntl1">
Name: <input ng-model="name" type="text"/>
<button ng-click="greet()">Greet</button>
</div>
</body>
</html>
script.js
function Cntl1($window, $scope){
$scope.name = 'World';
$scope.greet = function() {
($window.mockWindow || $window).alert('Hello ' + $scope.name);
}
}
End to end test
it('should calculate expression in binding', function() {
var alertText;
this.addFutureAction('set mock', function($window, $document, done) {
$window.mockWindow = {
alert: function(text){ alertText = text; }
};
done();
});
element(':button:contains(Greet)').click();
expect(this.addFuture('alert text', function(done) {
done(null, alertText);
})).toBe('Hello World');
});
jsfiddle link ?
表达式对于undefined
和null
是友好的。在Javascript中,对于表达式a.b.c
,如果a
不是一个对象,则会抛出异常。虽然这么做对于一个通用语言来说是很有意义的,但对于表达式求值这种主要用来绑定数据的用法来说,意义不大而且很繁琐。
因为我们经常会写下这样的代码:
{{a.b.c}}
当a
为undefined
的时候(可能我们正在等服务器端返回数据),什么也不显示通常更有意义一些。如果我们不这么处理,上面的代码就必须写成这种让人头晕的形式:
{{((a||{}).b||{}).c}}
同样,调用一个undefined
或null
上的函数,如a.b.c()
,也会简单地返回undefined
.
在angularjs表达式中,不能使用如条件判断、循环或抛异常这种控制语句。主要原因是这种通常与业务逻辑相关的代码,应该放在controller中,而不是view中。
如果你的确需要,可以在controller中定义一个javascript函数,再在angularjs表达式中调用它。
name | uppercase
name的值将会直接传给uppercase
这个filter.
Filter还可以串起来:
value | filter1 | filter2
你可还以使用冒号:
向filter传参数。比如,把数字123
显示为两个小数点的形式:
123 | number:2
你可能会注意到,有一些对象或者方法,它们以$
开头,是什么意思?这是一个默认约定,由angularjs提供的api以$
开头,与其它相区别。
所以我们在定义变量或者函数时,不要使用$
开头,以免跟angularjs冲突。