Improve this doc

Angular Expressions

Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as {{ expression }}.

For example, these are valid expressions in Angular:

Angular Expressions vs. JavaScript Expressions

Angular expressions are like JavaScript expressions with the following differences:

If you want to run more complex JavaScript code, you should make it a controller method and call the method from your view. If you want to eval() an Angular expression yourself, use the $eval() method.

Example

  Edit in Plunker
1+2={{1+2}}
it('should calculate expression in binding', function() {
  expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
});

You can try evaluating different expressions here:

  Edit in Plunker
<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>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
    </li>
  </ul>
</div>
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(by.css('.expressions button')).click();
  var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
  expect(lis.count()).toBe(1);
  expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
});

Context

Angular does not use JavaScript's eval() to evaluate expressions. Instead Angular's $parse service processes these expressions.

Angular expressions do not have access to global variables like window, document or location. This restriction is intentional. It prevents accidental access to the global state – a common source of subtle bugs.

Instead use services like $window and $location in functions called from expressions. Such services provide mockable access to globals.

  Edit in Plunker
<div class="example2" ng-controller="Cntl1">
  Name: <input ng-model="name" type="text"/>
  <button ng-click="greet()">Greet</button>
  <button ng-click="window.alert('Should not see me')">Won't greet</button>
</div>
function Cntl1($window, $scope){
  $scope.name = 'World';

  $scope.greet = function() {
    $window.alert('Hello ' + $scope.name);
  };
}
it('should calculate expression in binding', function() {
  if (browser.params.browser == 'safari') {
    // Safari can't handle dialogs.
    return;
  }
  element(by.css('[ng-click="greet()"]')).click();

  var alertDialog = browser.switchTo().alert();

  expect(alertDialog.getText()).toEqual('Hello World');

  alertDialog.accept();
});

Forgiving

Expression evaluation is forgiving to undefined and null. In JavaScript, evaluating a.b.c throws an exception if a is not an object. While this makes sense for a general purpose language, the expression evaluations are primarily used for data binding, which often look like this:

    {{a.b.c}}

It makes more sense to show nothing than to throw an exception if a is undefined (perhaps we are waiting for the server response, and it will become defined soon). If expression evaluation wasn't forgiving we'd have to write bindings that clutter the code, for example: {{((a||{}).b||{}).c}}

Similarly, invoking a function a.b.c() on undefined or null simply returns undefined.

No Control Flow Statements

Apart from the ternary operator (a ? b : c), you cannot write a control flow statement in an expression. The reason behind this is core to the Angular philosophy that application logic should be in controllers, not the views. If you need a real conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.

$event

Directives like ngClick and ngFocus expose a $event object within the scope of that expression.

  Edit in Plunker
<div ng-controller="EventController">
  <button ng-click="clickMe($event)">Event</button>
  <p><code>$event</code>: <pre> {{$event | json}}</pre></p>
  <p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p>
</div>
angular.module('eventExampleApp', []).
  controller('EventController', ['$scope', function($scope) {
    /*
     * expose the event object to the scope
     */
    $scope.clickMe = function(clickEvent) {
      $scope.clickEvent = simpleKeys(clickEvent);
      console.log(clickEvent);
    };

    /*
     * return a copy of an object with only non-object keys
     * we need this to avoid circular references
     */
    function simpleKeys (original) {
      return Object.keys(original).reduce(function (obj, key) {
        obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
        return obj;
      }, {});
    }
  }]);

Note in the example above how we can pass in $event to clickMe, but how it does not show up in {{$event}}. This is because $event is outside the scope of that binding.