mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-03-30 23:18:53 +08:00
add paragraphs about function declaration, comma and void operators and RegExp to the diff list in the beginning of articule Closes #10418 Closes #10452
222 lines
7.6 KiB
Plaintext
222 lines
7.6 KiB
Plaintext
@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.
|