天天看點

AngularJS開發指南06:表達式

表達式是類似Javascript的代碼片段,通常在綁定中用到,寫在雙大括号中如

{{表達式}}

。表達式是用

$parse

方法來處理的。

下面是一些合法的AngularJS表達式

  • 1+2

  • 3*10 | currency

  • user.name

AngularJS表達式 與Javascript表達式的比較

你可能會認為AngularJS視圖中的表達式就是Javascript表達式,這種認識不完全對,因為AngularJS不會用Javascript的

eval()

函數去執行表達式。

不過除了以下幾個需要差別的地方以外,你可以把AngularJS表達式看成是Javascript表達式: formatDate

  • 屬性表達式:屬性表達式是對應于目前的作用域的,不像Javascript對應的是全局window對象。
  • 允許未定義值:執行表達式時,AngularJS能夠允許undefined或者null,不像Javascript會抛出一個異常。
  • 沒有控制結構: 你不能在AngularJS表達式中使用“條件判斷”、“循環”、“抛出異常”等控制結構。
  • 過濾器(類似unix中的管道操作符): 你可以通過過濾器鍊來傳遞表達式的結果。例如将日期對象轉變成指定的閱讀友好的格式。

如果你想要在表達式中使用标準的Javascript,那麼你應該把它寫成一個控制器的方法,然後在表達式中調用這個方法。如果你想在Javascript中執行AngularJS表達式,你可以使用

$eval()

方法。

例子

index.html:

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.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/angular-1.0.2.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);
  };
}           
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"]);
});           

屬性表達式

屬性表達式計算是發生在作用域中的。Javascript預設是以window為作用域的。AngularJS要使用window作用域的話得用

$window

來指向全局window對象。 比如說,你使用window中定義的

alert()

方法,在AngularJS表達式中必須寫成

$window.alert()

才行。這是為了防止意外進入全局作用域(各種bug的來源)而設計的。

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/angular-1.0.2.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');
});           

允許未定義值

表達式在執行時是可以允許

undifined

null

的。

在Javascript中,計算a.b.c會抛出一個異常,如果這不是一個對象的話。這對大部分的語言來說是有意義的,但是如果大多數時候表達式是用來作數綁定,像下面這種形式:

{{a.b.c}}           

那麼表達式傳回一個空值會比觸發異常更有意義。因為通常我們是在等待伺服器的響應,并且變量馬上就會被定義和指派。如果表達式不能容忍未定義的值,那麼我們綁定的代碼就不得不寫成形如:

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

執行未定義的函數

a.b.c()

也會傳回undefined,不會觸發異常。

沒有流程控制結構

你不能在表達式中使用控制結構。這樣設計得原因在于AngularJS的設計理念之一就是邏輯代碼都應該在控制器裡。如果你需要使用條件、循環、或者處理異常,你就應該寫在控制器的方法裡。

過濾器

當将資料呈獻給使用者時,你很可能需要将資料轉換為閱讀友好的格式。比方說,你可能需要在顯示之前将一個日期對象轉換為使用者本地的時間格式。你可以用鍊式的過濾器來傳遞表達式,像下面這樣:

name | uppercase           

這個表達式會将

name

的值傳遞給

uppercase

這個過濾器。

鍊式過濾器使用的是下面這樣的文法:

value | filter1 | filter2           

你也可以通過冒号來給過濾器傳遞參數,比如,将123顯示成帶有兩位小數的形式:

123 | number:2           

$符号

你可能會好奇,這個$的字首有什麼用?其實這隻是一個标記AngularJS專有屬性方法的符号,用來表示差別于其他使用者自定義函數的符号。如果AngularJS不用$,那麼執行

a.length()

會傳回undeifined,因為a和angular都沒有這樣一個屬性。

但是考慮到我們以後可能會增加一個length的方法(這會改變表達式的行為),同時你也想要自己增加一個length方法的話,那就會産生沖突。這種沖突可能出現都是因為,AngularJS的設計是在已有的對象上添加行為。使用$做字首的話,就能使得開發者的代碼和AngularJS和諧共處了。

繼續閱讀