"表达式"是一种类似JavaScript的代码片段,通常在视图中以''的形式使用。表达式由$parse服务解析,而解析之后经常会使用过滤器来格式化成一种更加用户友好的形式。

例如,下面这些都是Angular中合法的表达式:

  • 1+2
  • user.name

Angular表达式与JS表达式

可能会有人认为Angular视图表达式就是JavaScript表达式,但这不完全正确,因为Angular并没有使用JavaScript中的'eval()'来解析表达式。你可以认为Angular表达式与JavaScript表达式有如下的区别:

  • 属性解析: 所有的属性的解析都是相对于作用域(scope)的,而不像JavaScript中的表达式解析那样是相对于全局'window'对象的。

  • 容错性: 表达式的解析对'undefined'和'null'具有容错性,这不像在JavaScript中,试图解析未定义的属性时会抛出ReferenceErrorTypeError错误.

  • 禁止控制流语句: 表达式中不允许包括下列语句:条件判断(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'对象。例如,如果你想调用在'window'上定义的'alert()'方法,在表达式中,你必须使用'$window.alert()'。作者故意这样设定,就是为了防止对全局状态非正常的访问(一些奇怪bug的常见来源)。

源码

<!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方法中执行。