Permalink
Comparing changes
Open a pull request
- 6 commits
- 11 files changed
- 0 commit comments
- 4 contributors
Commits on Dec 31, 2015
- Move interpolation info from Directive guide into new interpolation guide - Add information about boolean attributes to interpolation guide - remove wroong examples from prefixed boolean attribute docs, link to interpolation guide instead - mention additional examples for attributes that benefit from ngAttr - add docs for ngRequired directive
Unified
Split
Showing
with
416 additions
and 115 deletions.
- +0 −57 docs/content/guide/directive.ngdoc
- +4 −3 docs/content/guide/expression.ngdoc
- +142 −0 docs/content/guide/interpolation.ngdoc
- +3 −3 docs/content/tutorial/index.ngdoc
- +2 −2 docs/content/tutorial/step_05.ngdoc
- +2 −2 docs/content/tutorial/step_06.ngdoc
- +2 −1 src/ng/compile.js
- +20 −43 src/ng/directive/attrs.js
- +238 −1 src/ng/directive/validators.js
- +2 −2 src/ng/interpolate.js
- +1 −1 src/ngResource/resource.js
| @@ -141,63 +141,6 @@ directives when possible. | ||
| </div> | ||
|
|
||
|
|
||
|
|
||
| ### Text and attribute bindings | ||
|
|
||
| During the compilation process the {@link ng.$compile compiler} matches text and attributes | ||
| using the {@link ng.$interpolate $interpolate} service to see if they contain embedded | ||
| expressions. These expressions are registered as {@link ng.$rootScope.Scope#$watch watches} | ||
| and will update as part of normal {@link ng.$rootScope.Scope#$digest digest} cycle. An | ||
| example of interpolation is shown below: | ||
|
|
||
| ```html | ||
| <a ng-href="img/{{username}}.jpg">Hello {{username}}!</a> | ||
| ``` | ||
|
|
||
|
|
||
| ### `ngAttr` attribute bindings | ||
|
|
||
| Web browsers are sometimes picky about what values they consider valid for attributes. | ||
|
|
||
| For example, considering this template: | ||
|
|
||
| ```html | ||
| <svg> | ||
| <circle cx="{{cx}}"></circle> | ||
| </svg> | ||
| ``` | ||
|
|
||
| We would expect Angular to be able to bind to this, but when we check the console we see | ||
| something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's | ||
| restrictions, you cannot simply write `cx="{{cx}}"`. | ||
|
|
||
| With `ng-attr-cx` you can work around this problem. | ||
|
|
||
| If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`) | ||
| then during the binding it will be applied to the corresponding unprefixed attribute. This allows | ||
| you to bind to attributes that would otherwise be eagerly processed by browsers | ||
| (e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of | ||
| {@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string | ||
| results in `undefined`, the attribute is removed and not added to the element. | ||
|
|
||
| For example, we could fix the example above by instead writing: | ||
|
|
||
| ```html | ||
| <svg> | ||
| <circle ng-attr-cx="{{cx}}"></circle> | ||
| </svg> | ||
| ``` | ||
|
|
||
| If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind to is naturally camelcased. | ||
|
|
||
| For example, to bind to `viewBox`, we can write: | ||
|
|
||
| ```html | ||
| <svg ng-attr-view_box="{{viewBox}}"> | ||
| </svg> | ||
| ``` | ||
|
|
||
|
|
||
| ## Creating Directives | ||
|
|
||
| First let's talk about the {@link ng.$compileProvider#directive API for registering directives}. Much like | ||
| @@ -5,8 +5,9 @@ | ||
|
|
||
| # Angular Expressions | ||
|
|
||
| Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as | ||
| `{{ expression }}`. | ||
| Angular expressions are JavaScript-like code snippets that are mainly placed in | ||
| interpolation bindings such as `<span title="{{ attrBinding }}">{{ textBinding }}</span>`, | ||
| but also used directly in directive attributes such as `ng-click="functionExpression()"`. | ||
|
|
||
| For example, these are valid expressions in Angular: | ||
|
|
||
| @@ -285,7 +286,7 @@ result is a non-undefined value (see value stabilization algorithm below). | ||
| </example> | ||
|
|
||
|
|
||
| ### Why this feature | ||
| ### Reasons for using one-time binding | ||
|
|
||
| The main purpose of one-time binding expression is to provide a way to create a binding | ||
| that gets deregistered and frees up resources once the binding is stabilized. | ||
| @@ -0,0 +1,142 @@ | ||
| @ngdoc overview | ||
| @name Interpolation | ||
| @sortOrder 275 | ||
| @description | ||
|
|
||
| # Interpolation and data-binding | ||
|
|
||
| Interpolation markup with embedded @link {guide/expressions expressions} is used by Angular to | ||
| provide data-binding to text nodes and attribute values. | ||
|
|
||
| An example of interpolation is shown below: | ||
|
|
||
| ```html | ||
| <a ng-href="img/{{username}}.jpg">Hello {{username}}!</a> | ||
| ``` | ||
|
|
||
| ### How text and attribute bindings work | ||
|
|
||
| During the compilation process the {@link ng.$compile compiler} uses the {@link ng.$interpolate $interpolate} | ||
| service to see if text nodes and element attributes contain interpolation markup with embedded expressions. | ||
|
|
||
| If that is the case, the compiler adds an interpolateDirective to the node and | ||
| registers {@link ng.$rootScope.Scope#$watch watches} on the computed interpolation function, | ||
| which will update the corresponding text nodes or attribute values as part of the | ||
| normal {@link ng.$rootScope.Scope#$digest digest} cycle. | ||
|
|
||
| Note that the interpolateDirective has a priority of 100 and sets up the watch in the preLink function. | ||
|
|
||
| ### Binding to boolean attributes | ||
|
|
||
| Attributes such as `disabled` are called `boolean` attributes, because their presence means `true` and | ||
| their absence means `false`. We cannot use normal attribute bindings with them, because the HTML | ||
| specification does not require browsers to preserve the values of boolean attributes. This means that | ||
| if we put an Angular interpolation expression into such an attribute then the binding information | ||
| would be lost, because the browser ignores the attribute value. | ||
|
|
||
| In the following example, the interpolation information would be ignored and the browser would simply | ||
| interpret the attribute as present, meaning that the button would always be disabled. | ||
|
|
||
| ```html | ||
| Disabled: <input type="checkbox" ng-model="isDisabled" /> | ||
| <button disabled="{{isDisabled}}">Disabled</button> | ||
| ``` | ||
|
|
||
| For this reason, Angular provides special `ng`-prefixed directives for the following boolean attributes: | ||
| {@link ngDisabled `disabled`}, [@link ngRequired `required`}, [@link ngSelected `selected`}, | ||
| {@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and [@link ngOpen `open`}. | ||
|
|
||
| These directives take an expression inside the attribute, and set the corresponding boolean attribute | ||
| to true when the expression evaluates to truthy. | ||
|
|
||
| ```html | ||
| Disabled: <input type="checkbox" ng-model="isDisabled" /> | ||
| <button ng-disabled="isDisabled">Disabled</button> | ||
| ``` | ||
|
|
||
| ### `ngAttr` for binding to arbitrary attributes | ||
|
|
||
| Web browsers are sometimes picky about what values they consider valid for attributes. | ||
|
|
||
| For example, considering this template: | ||
|
|
||
| ```html | ||
| <svg> | ||
| <circle cx="{{cx}}"></circle> | ||
| </svg> | ||
| ``` | ||
|
|
||
| We would expect Angular to be able to bind to this, but when we check the console we see | ||
| something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's | ||
| restrictions, you cannot simply write `cx="{{cx}}"`. | ||
|
|
||
| With `ng-attr-cx` you can work around this problem. | ||
|
|
||
| If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`) | ||
| then during the binding it will be applied to the corresponding unprefixed attribute. This allows | ||
| you to bind to attributes that would otherwise be eagerly processed by browsers | ||
| (e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of | ||
| {@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string | ||
| results in `undefined`, the attribute is removed and not added to the element. | ||
|
|
||
| For example, we could fix the example above by instead writing: | ||
|
|
||
| ```html | ||
| <svg> | ||
| <circle ng-attr-cx="{{cx}}"></circle> | ||
| </svg> | ||
| ``` | ||
|
|
||
| If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), | ||
| such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind | ||
| to is naturally camelcased. | ||
|
|
||
| For example, to bind to `viewBox`, we can write: | ||
|
|
||
| ```html | ||
| <svg ng-attr-view_box="{{viewBox}}"> | ||
| </svg> | ||
| ``` | ||
|
|
||
| The following attributes are also known to cause problems when used with normal bindings: | ||
|
|
||
| - **size** in `<select>` elements (see [Github issue 1619](https://github.com/angular/angular.js/issues/1619)) | ||
| - **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [Github issue 5025](https://github.com/angular/angular.js/issues/5025)) | ||
|
|
||
|
|
||
| ### Embedding interpolation markup inside expressions | ||
|
|
||
| Angular directives take either expressions or interpolation markup with embedded expressions. So the | ||
| following example which embeds interpolation inside an expression is a bad practice: | ||
|
|
||
| ```html | ||
| <div ng-show="form{{$index}}.$invalid"></div> | ||
| ``` | ||
|
|
||
| You should instead delegate the computation of complex expressions to the scope, like this: | ||
|
|
||
| ```html | ||
| <div ng-show="getForm($index).$invalid"></div> | ||
| ``` | ||
|
|
||
| ```js | ||
| function getForm() { | ||
| return $scope['form' + $index]; | ||
| } | ||
| ``` | ||
|
|
||
| You can also access the `scope` with `this` in your templates: | ||
|
|
||
| ```html | ||
| <div ng-show="this['form' + $index].$invalid"></div> | ||
| ``` | ||
|
|
||
| #### Why mixing interpolation and expressions is bad practice: | ||
|
|
||
| - It increases the complexity of the markup | ||
| - There is no guarantee that it works for every directive, because interpolation itself is a directive. | ||
| If another directive accesses attribute data before interpolation has run, it will get the raw | ||
| interpolation markup and not data. | ||
| - It impacts performance, as interpolation adds another watcher to the scope. | ||
| - Since this is not recommended usage, we do not test for this, and changes to | ||
| Angular core may break your code. |
| @@ -61,7 +61,7 @@ a few git commands. | ||
|
|
||
| ### Install Git | ||
|
|
||
| You can download and install Git from http://git-scm.com/download. Once installed you should have | ||
| You can download and install Git from http://git-scm.com/download. Once installed, you should have | ||
| access to the `git` command line tool. The main commands that you will need to use are: | ||
|
|
||
| - `git clone ...` : clone a remote repository onto your local machine | ||
| @@ -123,7 +123,7 @@ npm --version | ||
| </a>. | ||
| </div> | ||
|
|
||
| Once you have Node.js installed on your machine you can download the tool dependencies by running: | ||
| Once you have Node.js installed on your machine, you can download the tool dependencies by running: | ||
|
|
||
| ``` | ||
| npm install | ||
| @@ -198,7 +198,7 @@ http://localhost:8000/app/index.html | ||
| ``` | ||
|
|
||
| <div class="alert alert-info"> | ||
| To serve the web app on a different ip address or port, edit the "start" script within package.json. | ||
| To serve the web app on a different IP address or port, edit the "start" script within package.json. | ||
| You can use `-a` to set the address and `-p` to set the port. | ||
| </div> | ||
|
|
||
| @@ -65,7 +65,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope, $http) { | ||
| `$http` makes an HTTP GET request to our web server, asking for `phones/phones.json` (the url is | ||
| relative to our `index.html` file). The server responds by providing the data in the json file. | ||
| (The response might just as well have been dynamically generated by a backend server. To the | ||
| browser and our app they both look the same. For the sake of simplicity we used a json file in this | ||
| browser and our app, they both look the same. For the sake of simplicity, we used a json file in this | ||
| tutorial.) | ||
|
|
||
| The `$http` service returns a {@link ng.$q promise object} with a `success` | ||
| @@ -114,7 +114,7 @@ as strings, which will not get minified. There are two ways to provide these inj | ||
|
|
||
| * Create a `$inject` property on the controller function which holds an array of strings. | ||
| Each string in the array is the name of the service to inject for the corresponding parameter. | ||
| In our example we would write: | ||
| In our example, we would write: | ||
|
|
||
| ```js | ||
| function PhoneListCtrl($scope, $http) {...} | ||
| @@ -59,8 +59,8 @@ the element attribute. | ||
| We also added phone images next to each record using an image tag with the {@link | ||
| ng.directive:ngSrc ngSrc} directive. That directive prevents the | ||
| browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to | ||
| invalid URL `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only | ||
| specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). | ||
| an invalid URL `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had | ||
| only specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`). | ||
| Using the `ngSrc` directive prevents the browser from making an http request to an invalid location. | ||
|
|
||
|
|
||
| @@ -1228,7 +1228,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | ||
| * @param {string} key Normalized key. (ie ngAttribute) . | ||
| * @param {function(interpolatedValue)} fn Function that will be called whenever | ||
| the interpolated value of the attribute changes. | ||
| * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info. | ||
| * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation | ||
| * guide} for more info. | ||
| * @returns {function()} Returns a deregistration function for this observer. | ||
| */ | ||
| $observe: function(key, fn) { | ||
| @@ -163,20 +163,7 @@ | ||
| * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. | ||
| * | ||
| * A special directive is necessary because we cannot use interpolation inside the `disabled` | ||
| * attribute. The following example would make the button enabled on Chrome/Firefox | ||
| * but not on older IEs: | ||
| * | ||
| * ```html | ||
| * <!-- See below for an example of ng-disabled being used correctly --> | ||
| * <div ng-init="isDisabled = false"> | ||
| * <button disabled="{{isDisabled}}">Disabled</button> | ||
| * </div> | ||
| * ``` | ||
| * | ||
| * This is because the HTML specification does not require browsers to preserve the values of | ||
| * boolean attributes such as `disabled` (Their presence means true and their absence means false.) | ||
| * If we put an Angular interpolation expression into such an attribute then the | ||
| * binding information would be lost when the browser removes the attribute. | ||
| * attribute. See the {@link guide/interpolation interpolation guide} for more info. | ||
| * | ||
| * @example | ||
| <example> | ||
| @@ -211,15 +198,9 @@ | ||
| * Note that this directive should not be used together with {@link ngModel `ngModel`}, | ||
| * as this can lead to unexpected behavior. | ||
| * | ||
| * ### Why do we need `ngChecked`? | ||
| * A special directive is necessary because we cannot use interpolation inside the `checked` | ||
| * attribute. See the {@link guide/interpolation interpolation guide} for more info. | ||
| * | ||
| * The HTML specification does not require browsers to preserve the values of boolean attributes | ||
| * such as checked. (Their presence means true and their absence means false.) | ||
| * If we put an Angular interpolation expression into such an attribute then the | ||
| * binding information would be lost when the browser removes the attribute. | ||
| * The `ngChecked` directive solves this problem for the `checked` attribute. | ||
| * This complementary directive is not removed by the browser and so provides | ||
| * a permanent reliable place to store the binding information. | ||
| * @example | ||
| <example> | ||
| <file name="index.html"> | ||
| @@ -248,13 +229,12 @@ | ||
| * @priority 100 | ||
| * | ||
| * @description | ||
| * The HTML specification does not require browsers to preserve the values of boolean attributes | ||
| * such as readonly. (Their presence means true and their absence means false.) | ||
| * If we put an Angular interpolation expression into such an attribute then the | ||
| * binding information would be lost when the browser removes the attribute. | ||
| * The `ngReadonly` directive solves this problem for the `readonly` attribute. | ||
| * This complementary directive is not removed by the browser and so provides | ||
| * a permanent reliable place to store the binding information. | ||
| * | ||
| * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy. | ||
| * | ||
| * A special directive is necessary because we cannot use interpolation inside the `readOnly` | ||
| * attribute. See the {@link guide/interpolation interpolation guide} for more info. | ||
| * | ||
| * @example | ||
| <example> | ||
| <file name="index.html"> | ||
| @@ -283,13 +263,11 @@ | ||
| * @priority 100 | ||
| * | ||
| * @description | ||
| * The HTML specification does not require browsers to preserve the values of boolean attributes | ||
| * such as selected. (Their presence means true and their absence means false.) | ||
| * If we put an Angular interpolation expression into such an attribute then the | ||
| * binding information would be lost when the browser removes the attribute. | ||
| * The `ngSelected` directive solves this problem for the `selected` attribute. | ||
| * This complementary directive is not removed by the browser and so provides | ||
| * a permanent reliable place to store the binding information. | ||
| * | ||
| * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy. | ||
| * | ||
| * A special directive is necessary because we cannot use interpolation inside the `selected` | ||
| * attribute. See the {@link guide/interpolation interpolation guide} for more info. | ||
| * | ||
| * @example | ||
| <example> | ||
| @@ -321,13 +299,12 @@ | ||
| * @priority 100 | ||
| * | ||
| * @description | ||
| * The HTML specification does not require browsers to preserve the values of boolean attributes | ||
| * such as open. (Their presence means true and their absence means false.) | ||
| * If we put an Angular interpolation expression into such an attribute then the | ||
| * binding information would be lost when the browser removes the attribute. | ||
| * The `ngOpen` directive solves this problem for the `open` attribute. | ||
| * This complementary directive is not removed by the browser and so provides | ||
| * a permanent reliable place to store the binding information. | ||
| * | ||
| * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy. | ||
| * | ||
| * A special directive is necessary because we cannot use interpolation inside the `open` | ||
| * attribute. See the {@link guide/interpolation interpolation guide} for more info. | ||
| * | ||
| * @example | ||
| <example> | ||
| <file name="index.html"> | ||
Oops, something went wrong.