Files
angular.js/docs/content/guide/expression.ngdoc
Peter Bacon Darwin f2c87672c5 docs(guide/expression): update diff list between JavaScript and Angular expressions
add paragraphs about function declaration, comma and void operators and
RegExp to the diff list in the beginning of articule

Closes #10418
Closes #10452
2014-12-16 14:33:40 +00:00

222 lines
7.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@ngdoc overview
@name Expressions
@sortOrder 270
@description
# 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:
* `1+2`
* `a+b`
* `user.name`
* `items[index]`
## Angular Expressions vs. JavaScript Expressions
Angular expressions are like JavaScript expressions with the following differences:
* **Context:** JavaScript expressions are evaluated against the global `window`.
In Angular, expressions are evaluated against a {@link ng.$rootScope.Scope `scope`} object.
* **Forgiving:** In JavaScript, trying to evaluate undefined properties generates `ReferenceError`
or `TypeError`. In Angular, expression evaluation is forgiving to `undefined` and `null`.
* **No Control Flow Statements:** You cannot use the following in an Angular expression:
conditionals, loops, or exceptions.
* **No Function Declarations:** You cannot decleare functions in an Angular expression.
Even inside `ng-init` directive
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
displaying it.
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
{@link ng.$rootScope.Scope#$eval `$eval()`} method.
## Example
<example>
<file name="index.html">
<span>
1+2={{1+2}}
</span>
</file>
<file name="protractor.js" type="protractor">
it('should calculate expression in binding', function() {
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
});
</file>
</example>
You can try evaluating different expressions here:
<example module="expressionExample">
<file name="index.html">
<div ng-controller="ExampleController" 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>
</file>
<file name="script.js">
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($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);
};
}]);
</file>
<file name="protractor.js" type="protractor">
it('should allow user expression testing', function() {
element(by.css('.expressions button')).click();
var lis = element(by.css('.expressions ul')).all(by.repeater('expr in exprs'));
expect(lis.count()).toBe(1);
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
});
</file>
</example>
## Context
Angular does not use JavaScript's `eval()` to evaluate expressions. Instead Angular's
{@link ng.$parse $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.
<example module="expressionExample">
<file name="index.html">
<div class="example2" ng-controller="ExampleController">
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>
</file>
<file name="script.js">
angular.module('expressionExample', [])
.controller('ExampleController', ['$window', '$scope', function($window, $scope) {
$scope.name = 'World';
$scope.greet = function() {
$window.alert('Hello ' + $scope.name);
};
}]);
</file>
<file name="protractor.js" type="protractor">
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();
});
</file>
</example>
## 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.
## No function declarations or RegExp creation with literal notation
You can't declare functions or create regular expressions from within AngularJS expressions. This is
to avoid complex model transformation logic inside templates. Such logic is better placed in a
controller or in a dedicated filter where it can be tested properly.
## `$event`
Directives like {@link ng.directive:ngClick `ngClick`} and {@link ng.directive:ngFocus `ngFocus`}
expose a `$event` object within the scope of that expression.
<example module="eventExampleApp">
<file name="index.html">
<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>
</file>
<file name="script.js">
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;
}, {});
}
}]);
</file>
</example>
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.