"表达式"是一种类似JavaScript的代码片段,通常在视图中以''的形式使用。表达式由$parse服务解析,而解析之后经常会使用过滤器来格式化成一种更加用户友好的形式。
例如,下面这些都是Angular中合法的表达式:
1+2user.name可能会有人认为Angular视图表达式就是JavaScript表达式,但这不完全正确,因为Angular并没有使用JavaScript中的'eval()'来解析表达式。你可以认为Angular表达式与JavaScript表达式有如下的区别:
属性解析: 所有的属性的解析都是相对于作用域(scope)的,而不像JavaScript中的表达式解析那样是相对于全局'window'对象的。
容错性: 表达式的解析对'undefined'和'null'具有容错性,这不像在JavaScript中,试图解析未定义的属性时会抛出ReferenceError或TypeError错误.
禁止控制流语句: 表达式中不允许包括下列语句:条件判断(if),循环(for/while),抛出异常(throw)。
另一方面,如果你想执行特定的JavaScript代码,你应该在一个控制器里导出一个方法,然后在模板中调用这个方法。如果你想在JavaScript中解析一个Angular表达式,使用方法。$eval()
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.2.25/angular.min.js"></script>
</head>
<body>
1+2={{1+2}}
</body>
</html>
it('should calculate expression in binding', function() {
expect(binding('1+2')).toEqual('3');
});
你可以在此处试一试解析其他的表达式:
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.2.25/angular.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 track by $index">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<tt></tt> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
</body>
</html>
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);
};
}
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"]);
});
属性解析发生在scope上。不像在JavaScript中,将默认的属性解析放在全局的window对象中,Angular表达式必须使用来指向全局的'window'对象。例如,如果你想调用在'window'上定义的'alert()'方法,在表达式中,你必须使用'$window.alert()'。作者故意这样设定,就是为了防止对全局状态非正常的访问(一些奇怪bug的常见来源)。$window
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/1.2.25/angular.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>
function Cntl1($window, $scope){
$scope.name = 'World';
$scope.greet = function() {
($window.mockWindow || $window).alert('Hello ' + $scope.name);
}
}
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');
});
表达式的解析对undefined和null具有容错性。在JavaScript中,解析'a,b,c'时,如果'a'不是一个对象会抛出异常。在一些语言中这可能会有用,但表达式的解析在Angular中主要用在数据绑定上,我们会像下面这样使用表达式:
{{a.b.c}}
此时如果'a'是未定义的(也许我们正等着服务器响应数据,在不久的将来其会被定义),解析抛出异常将不再合理。如果表达式不具有容错性的话,我们的代码会变的繁杂且影响阅读,例如:{{((a||{}).b||{}).c}}。
类似的,在undefined和null的对象上调用方法'a.b.c()'时会返回undefined。
在表达式中禁止写控制流语句。这背后的原因在于,Angular设计哲学的核心认为,应用逻辑应该在控制器中,而不是在视图中控制。如果你需要条件表达式,循环或者抛出异常出现在你的视图表达式中,请将其委托到JavaScript方法中执行。