From 7f1e2e48467f80cc083d24b44f088620e4e7bcb6 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Mon, 6 Jun 2011 08:50:35 -0700 Subject: new batch of docs --- docs/content/guide/dev_guide.expressions.ngdoc | 270 +++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 docs/content/guide/dev_guide.expressions.ngdoc (limited to 'docs/content/guide/dev_guide.expressions.ngdoc') diff --git a/docs/content/guide/dev_guide.expressions.ngdoc b/docs/content/guide/dev_guide.expressions.ngdoc new file mode 100644 index 00000000..b0a268e6 --- /dev/null +++ b/docs/content/guide/dev_guide.expressions.ngdoc @@ -0,0 +1,270 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Understanding Angular Expressions +@description + + +Expressions are {@link dev_guide.templates.databinding bindings} that you write in HTML and embed +in templates in order to create views in angular. Angular expressions are similar but not +equivalent to JavaScript expressions. + + +For example, these are all valid expressions in angular: + + +* `1+2={{1+2}}` +* `3*10|currency` +* `Hello {{name}}!` +* `Hello {{'World'}}!` + + + + +## Angular Expressions vs. JS Expressions + + +It might be tempting to think of angular view expressions as JavaScript expressions, but that is +not entirely correct. Angular does not use a simple JavaScript eval of the expression text. You can +think of angular expressions as JavaScript expressions with these differences: + + +* **Attribute Evaluation:** evaluation of all attributes are against the current scope, not to the +global window as in JavaScript. +* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript. +* **No Control Flow Statements:** you cannot do the following from an angular expression: +conditionals, loops, or throw. +* **Type Augmentation:** the scope expression evaluator augments built-in types. +* **Filters:** you can add filters to an expression, for example to convert raw data into a +human-readable format. +* **The $:** angular reserves this prefix to differentiate its API names from others. + + +If, on the other hand, you do want to run arbitrary JavaScript code, you should make it a +controller method and call that. If you want to `eval()` an angular expression from JavaScript, use +the `Scope:$eval()` method. + + +## Example + + + 1+2={{1+2}} + + + it('should calculate expression in binding', function(){ + expect(binding('1+2')).toEqual('3'); + }); + + + + +You can try evaluating different expressions here: + + + + +
+ Expression: + + +
    +
  • + [ X ] + {{expr}} => +
  • +
+
+
+ + 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"]); + }); + +
+ + + + +# Attribute Evaluation + + +Evaluation of all attributes takes place against the current scope. Unlike JavaScript, where names +default to global window properties, angular expressions have to use `$window` to refer to the +global object. For example, if you want to call `alert()`, which is defined on `window`, an +expression must use `$window.alert()`. This is done intentionally to prevent accidental access to +the global state (a common source of subtle bugs). + + + + +
+ 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'); + }); + +
+ + +## 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. + + +Assignments work the same way in reverse: + + + a.b.c = 10 + + +...creates the intermediary objects even if a is undefined. + + + + +## No Control Flow Statements + + +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 in the view. If you need a +conditional (including ternary operators), loop, or to throw from a view expression, delegate to a +JavaScript method instead. + + + + +## Type Augmentation + + +Built-in types have methods like `[].push()`, but the richness of these methods is limited. +Consider the example below, which allows you to do a simple search over a canned set of contacts. +The example would be much more complicated if we did not have the `Array:$filter()`. There is no +built-in method on `Array` called {@link api/angular.array.filter $filter} and angular doesn't add +it to `Array.prototype` because that could collide with other JavaScript frameworks. + + +For this reason the scope expression evaluator augments the built-in types to make them act like +they have extra methods. The actual method for `$filter()` is `angular.Array.filter()`. You can +call it from JavaScript. + + +Extensions: You can further extend the expression vocabulary by adding new methods to +`angular.Array` or `angular.String`, etc. + + + + +
+ Search: + + + + + + +
NamePhone
{{friend.name}}{{friend.phone}}
+
+ + it('should filter the list', function(){ + var tr = using('table.example3').repeater('tr.ng-attr-widget'); + expect(tr.count()).toBe(5); + input('searchText').enter('a'); + expect(tr.count()).toBe(2); + + + }); + +
+ + +## Filters + + +When presenting data to the user, you might need to convert the data from its raw format to a +user-friendly format. For example, you might have a data object that needs to be formatted +according to the locale before displaying it to the user. You can pass expressions through a chain +of filters like this: + + + name | uppercase + + +The expression evaluator simply passes the value of name to angular.filter.uppercase. + + +Chain filters using this syntax: + + + value | filter1 | filter2 + + +You can also pass colon-delimited arguments to filters, for example, to display the number 123 with +2 decimal points: + + + 123 | number:2 + + +# The $ + + +You might be wondering, what is the significance of the $ prefix? It is simply a prefix that +angular uses, to differentiate its API names from others. If angular didn't use $, then evaluating +`a.length()` would return undefined because neither a nor angular define such a property. + + +Consider that in a future version of angular we might choose to add a length method, in which case +the behavior of the expression would change. Worse yet, you the developer could create a length +property and then we would have a collision. This problem exists because angular augments existing +objects with additional behavior. By prefixing its additions with $ we are reserving our namespace +so that angular developers and developers who use angular can develop in harmony without collisions. + + + + +## Related Topics + + +* {@link dev_guide.compiler.markup Understanding Angular Markup} +* {@link dev_guide.templates.filters Understanding Angular Filters} + + +## Related API + + +* {@link api/angular.compile Angular Compiler API} -- cgit v1.2.3