|
| 1 | +@ngdoc overview |
| 2 | +@name Interpolation |
| 3 | +@sortOrder 275 |
| 4 | +@description |
| 5 | + |
| 6 | +# Interpolation and data-binding |
| 7 | + |
| 8 | +Interpolation markup with embedded @link {guide/expressions expressions} is used by Angular to |
| 9 | +provide data-binding to text nodes and attribute values. |
| 10 | + |
| 11 | +An example of interpolation is shown below: |
| 12 | + |
| 13 | +```html |
| 14 | +<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a> |
| 15 | +``` |
| 16 | + |
| 17 | +### How text and attribute bindings work |
| 18 | + |
| 19 | +During the compilation process the {@link ng.$compile compiler} uses the {@link ng.$interpolate $interpolate} |
| 20 | +service to see if text nodes and element attributes contain interpolation markup with embedded expressions. |
| 21 | + |
| 22 | +If that is the case, the compiler adds an interpolateDirective to the node and |
| 23 | +registers {@link ng.$rootScope.Scope#$watch watches} on the computed interpolation function, |
| 24 | +which will update the corresponding text nodes or attribute values as part of the |
| 25 | +normal {@link ng.$rootScope.Scope#$digest digest} cycle. |
| 26 | + |
| 27 | +Note that the interpolateDirective has a priority of 100 and sets up the watch in the preLink function. |
| 28 | + |
| 29 | +### Binding to boolean attributes |
| 30 | + |
| 31 | +Attributes such as `disabled` are called `boolean` attributes, because their presence means `true` and |
| 32 | +their absence means `false`. We cannot use normal attribute bindings with them, because the HTML |
| 33 | +specification does not require browsers to preserve the values of boolean attributes. This means that |
| 34 | +if we put an Angular interpolation expression into such an attribute then the binding information |
| 35 | +would be lost, because the browser ignores the attribute value. |
| 36 | + |
| 37 | +In the following example, the interpolation information would be ignored and the browser would simply |
| 38 | +interpret the attribute as present, meaning that the button would always be disabled. |
| 39 | + |
| 40 | + ```html |
| 41 | + Disabled: <input type="checkbox" ng-model="isDisabled" /> |
| 42 | + <button disabled="{{isDisabled}}">Disabled</button> |
| 43 | +``` |
| 44 | + |
| 45 | +For this reason, Angular provides special `ng`-prefixed directives for the following boolean attributes: |
| 46 | +{@link ngDisabled `disabled`}, [@link ngRequired `required`}, [@link ngSelected `selected`}, |
| 47 | +{@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and [@link ngOpen `open`}. |
| 48 | + |
| 49 | +These directives take an expression inside the attribute, and set the corresponding boolean attribute |
| 50 | +to true when the expression evaluates to truthy. |
| 51 | + |
| 52 | + ```html |
| 53 | + Disabled: <input type="checkbox" ng-model="isDisabled" /> |
| 54 | + <button ng-disabled="isDisabled">Disabled</button> |
| 55 | +``` |
| 56 | + |
| 57 | +### `ngAttr` for binding to arbitrary attributes |
| 58 | + |
| 59 | +Web browsers are sometimes picky about what values they consider valid for attributes. |
| 60 | + |
| 61 | +For example, considering this template: |
| 62 | + |
| 63 | +```html |
| 64 | +<svg> |
| 65 | + <circle cx="{{cx}}"></circle> |
| 66 | +</svg> |
| 67 | +``` |
| 68 | + |
| 69 | +We would expect Angular to be able to bind to this, but when we check the console we see |
| 70 | +something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's |
| 71 | +restrictions, you cannot simply write `cx="{{cx}}"`. |
| 72 | + |
| 73 | +With `ng-attr-cx` you can work around this problem. |
| 74 | + |
| 75 | +If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`) |
| 76 | +then during the binding it will be applied to the corresponding unprefixed attribute. This allows |
| 77 | +you to bind to attributes that would otherwise be eagerly processed by browsers |
| 78 | +(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of |
| 79 | +{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string |
| 80 | +results in `undefined`, the attribute is removed and not added to the element. |
| 81 | + |
| 82 | +For example, we could fix the example above by instead writing: |
| 83 | + |
| 84 | +```html |
| 85 | +<svg> |
| 86 | + <circle ng-attr-cx="{{cx}}"></circle> |
| 87 | +</svg> |
| 88 | +``` |
| 89 | + |
| 90 | +If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), |
| 91 | +such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind |
| 92 | +to is naturally camelcased. |
| 93 | + |
| 94 | +For example, to bind to `viewBox`, we can write: |
| 95 | + |
| 96 | +```html |
| 97 | +<svg ng-attr-view_box="{{viewBox}}"> |
| 98 | +</svg> |
| 99 | +``` |
| 100 | + |
| 101 | +The following attributes are also known to cause problems when used with normal bindings: |
| 102 | + |
| 103 | +- **size** in `<select>` elements (see [Github issue 1619](https://github.com/angular/angular.js/issues/1619)) |
| 104 | +- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [Github issue 5025](https://github.com/angular/angular.js/issues/5025)) |
| 105 | + |
| 106 | + |
| 107 | +### Embedding interpolation markup inside expressions |
| 108 | + |
| 109 | +Angular directives take either expressions or interpolation markup with embedded expressions. So the |
| 110 | +following example which embeds interpolation inside an expression is a bad practice: |
| 111 | + |
| 112 | +```html |
| 113 | +<div ng-show="form{{$index}}.$invalid"></div> |
| 114 | +``` |
| 115 | + |
| 116 | +You should instead delegate the computation of complex expressions to the scope, like this: |
| 117 | + |
| 118 | +```html |
| 119 | +<div ng-show="getForm($index).$invalid"></div> |
| 120 | +``` |
| 121 | + |
| 122 | +```js |
| 123 | + function getForm() { |
| 124 | + return $scope['form' + $index]; |
| 125 | + } |
| 126 | +``` |
| 127 | + |
| 128 | +You can also access the `scope` with `this` in your templates: |
| 129 | + |
| 130 | +```html |
| 131 | +<div ng-show="this['form' + $index].$invalid"></div> |
| 132 | +``` |
| 133 | + |
| 134 | +#### Why mixing interpolation and expressions is bad practice: |
| 135 | + |
| 136 | +- It increases the complexity of the markup |
| 137 | +- There is no guarantee that it works for every directive, because interpolation itself is a directive. |
| 138 | +If another directive accesses attribute data before interpolation has run, it will get the raw |
| 139 | +interpolation markup and not data. |
| 140 | +- It impacts performance, as interpolation adds another watcher to the scope. |
| 141 | +- Since this is not recommended usage, we do not test for this, and changes to |
| 142 | +Angular core may break your code. |
0 commit comments