式(Expressions)

概要

式(Expressions)は、{{式}}のようにして配置するのJavaScriptライクなコードの断片です。 式は$parseサービスによって処理されます。

例えば、下記は全てAngular内で有効な式になります。

  • 1+2
  • 3*10 | currency
  • user.name

Angular式 vs JavaScript式

AngularのViewの式を、JavaScript式と同じものとして考えたくなるかもしれませんが、 AngularはJavaScriptのeval()で式を評価しているわけでは無いので、完全に同じではありません。 JavaScriptの式とAngularの式の違いについて、下記の事を考慮しておくと良いでしょう。

評価する値について
全てのプロパティの評価は、スコープに対して行われ、 グローバルのwindowに対して式が評価されるJavaScriptとは異なります。
許容する値について
Angularによる式の評価では、undefinednullは許容されます。 undefinedプロパティの評価を試みると、参照エラーまたは型エラーを発生させるjavaScripとは異なります。
フローの制御が不可能
Angular式内では、条件、ループ、スローを行うことは出来ません。
フィルター
フィルターチェインを通した式の評価の結果を得ることが可能です。 例えば、日付オブジェクトを人が読み取れる特定のローカルのフォーマットに変換します。

もし、任意のJavaScriptコードを実行したい場合は、 それをコントローラーメソッドにして、そのメソッドを呼び出すべきです。 もし、Angularの式をJavaScriptからeval()したい場合は、$eval()メソッドを使用します。

サンプル

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/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.0-rc.2/angular.min.js"></script>
  </head>
  <body>
    1+2={{1+2}}
  </body>
</html>

下記のデモは、入力欄から任意の式を試すことが可能です。

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/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">
         [ <a href="" ng-click="removeExp($index)">X</a> ]
         <tt>{{expr}}</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"]);
});
<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
<script>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);
  };
}
</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>

プロパティの評価

全てのプロパティの評価は、スコープに対してのものに置き換えられます。 デフォルトでグローバルのwindowのプロパティを参照するJavaScriptと異なり、 Angular式で、グローバルwinodwオブジェクトを参照するには$windowを使用しなければいけません。 例えば、もしwindowに定義されているalert()をAngular式内で呼び出したい場合、 $window.alert()を使用しなければいけません。 これはグローバル空間での衝突などの事故や見つけにくいバグを生み出す事を意図的に防ぐために行います。

<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/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');
});
<!doctype html>
<html ng-app>
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
<script>function Cntl1($window, $scope){
  $scope.name = 'World';

  $scope.greet = function() {
    ($window.mockWindow || $window).alert('Hello ' + $scope.name);
  }
}
</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>

許容する式について

Angular式はundefinednullの評価を許容します。 JavaScriptでは、もしaがオブジェクトで無ければ、a.b.cを評価した際に例外をスローします。 これは一般的な言語では理にかなっていますが、Angular式の評価の本来の目的は、下記のようなデータの紐付けに使用されるためです。

{{a.b.c}}

もし、undefinedであるならば、例外をスローするより何も表示しない方が理にかなっています。 (もしかしたら、サーバーレスポンスを待ち受けていて、そのうちに定義されるのかもしれないので) もし、このような許容を行わないと、例えば{{((a||{}).b||{}).c}}のような冗長なコードを書かなければいけなくなります。

同様にundefinedまたはnullのオブジェクト上で、a.b.c()を呼び出した場合、 単にundefinedを返します。

制御できないフロー式について

Angularのビューの式では、フローを制御する文を書くことは出来ません。 これはAngularのコアとなる、「アプリケーションロジックはビューではなくコントローラーに書くべき」という、Angularの哲学に基づくためです。 もし、条件、ループ、スローをビューの式で必要な場合は、代わりにJavaScriptに委譲します。

フィルターについて

ユーザーに対してデータが提供される際に、ユーザーにとって分かりやすいフォーマットに変換する必要があるかもしれません。 例えば、ユーザーに対して表示する前に、ロケールに沿ったデータにする必要あるかもしれません。 下記のように、フィルターのチェインを通して式に渡すことが可能です。

name | uppercase

この式の評価は、単にnameの値が渡され、大文字化のフィルターが適用されます。 チェインフィルターは、下記の文法で使用します。

value | filter1 | filter2

また、コロン区切りで引数をフィルターに渡すことが可能です。 例えば、下記は少数第2位までの数字123を表示します。

123 | number:2

$について

あなたは、$プレフィックが何なのか疑問に思ったかもしれません。 これは単にAngularが使用するプレフィックスで、その他のAPI名と区別するためです。 仮にAngularが$を使用しないとして、a.length()はAngularはそのようなプロパティを定義していないため、 undefinedを返します。

Angularの将来のバージョンを考慮すると、式の挙動を変更するようなケースで、 我々はlengthメソッドを追加するという選択をするかもしれません。 更に悪いことに、あなた(開発者)はlengthプロパティを作ることが可能で、これらは衝突することになるでしょう。 Angularがオブジェクトに対して振る舞いを追加するとなると、こういった問題に直面します。 我々は$プレフィックスを追加することによって名前空間の予約を行い、Angularの開発者とAngularを使用する開発者の間で、 名前の衝突を起こさずに開発が出来るようにしています。

 Back to top

© 2017 Google
Licensed under the Creative Commons Attribution License 3.0.

このページは、ページトップのリンク先のAngularJS公式ドキュメント内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • AngularJSの更新頻度が高いため、元のコンテンツと比べてドキュメントの情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。