docs: clean up formats in template syntax guide (#32197)

PR Close #32197
This commit is contained in:
Judy Bogart 2019-08-19 14:37:36 -07:00 committed by atscott
parent d953c1cee3
commit ef2047555a
1 changed files with 77 additions and 109 deletions

View File

@ -6,8 +6,7 @@
h4 .syntax { font-size: 100%; } h4 .syntax { font-size: 100%; }
</style> </style>
The Angular application manages what the user sees and can do, achieving this through the interaction of a The Angular application manages what the user sees and can do, achieving this through the interaction of a component class instance (the *component*) and its user-facing template.
component class instance (the *component*) and its user-facing template.
You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM). You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM).
In Angular, the component plays the part of the controller/viewmodel, and the template represents the view. In Angular, the component plays the part of the controller/viewmodel, and the template represents the view.
@ -85,12 +84,10 @@ converts the expression results to strings, and links them with neighboring lite
it assigns this composite interpolated result to an **element or directive property**. it assigns this composite interpolated result to an **element or directive property**.
You appear to be inserting the result between element tags and assigning it to attributes. You appear to be inserting the result between element tags and assigning it to attributes.
However, interpolation is a special syntax that Angular converts into a *property binding*.
<div class="alert is-helpful"> <div class="alert is-helpful">
However, interpolation is a special syntax that Angular converts into a
property binding.
If you'd like to use something other than `{{` and `}}`, you can If you'd like to use something other than `{{` and `}}`, you can
configure the interpolation delimiter via the configure the interpolation delimiter via the
[interpolation](api/core/Component#interpolation) [interpolation](api/core/Component#interpolation)
@ -124,8 +121,8 @@ including:
Other notable differences from JavaScript syntax include: Other notable differences from JavaScript syntax include:
* No support for the bitwise operators such as `|` and `&` * No support for the bitwise operators such as `|` and `&`
* New template expression operators, such as `|`, `?.` and `!` * New [template expression operators](guide/template-syntax#expression-operators), such as `|`, `?.` and `!`
<!-- link to: guide/template-syntax#expression-operators -->
### Expression context ### Expression context
@ -171,12 +168,29 @@ members of the expression context.
When using template expressions follow these guidelines: When using template expressions follow these guidelines:
* [No visible side effects](guide/template-syntax#no-visible-side-effects)
* [Quick execution](guide/template-syntax#quick-execution)
* [Simplicity](guide/template-syntax#simplicity) * [Simplicity](guide/template-syntax#simplicity)
* [Quick execution](guide/template-syntax#quick-execution)
* [No visible side effects](guide/template-syntax#no-visible-side-effects)
#### Simplicity
### No visible side effects Although it's possible to write complex template expressions, it's a better
practice to avoid them.
A property name or method call should be the norm, but an occasional Boolean negation, `!`, is OK.
Otherwise, confine application and business logic to the component,
where it is easier to develop and test.
#### Quick execution
Angular executes template expressions after every change detection cycle.
Change detection cycles are triggered by many asynchronous activities such as
promise resolutions, HTTP results, timer events, key presses and mouse moves.
Expressions should finish quickly or the user experience may drag, especially on slower devices.
Consider caching values when their computation is expensive.
#### No visible side effects
A template expression should not change any application state other than the value of the A template expression should not change any application state other than the value of the
target property. target property.
@ -187,40 +201,18 @@ The view should be stable throughout a single rendering pass.
An [idempotent](https://en.wikipedia.org/wiki/Idempotence) expression is ideal because An [idempotent](https://en.wikipedia.org/wiki/Idempotence) expression is ideal because
it is free of side effects and improves Angular's change detection performance. it is free of side effects and improves Angular's change detection performance.
In Angular terms, an idempotent expression always returns In Angular terms, an idempotent expression always returns
*exactly the same thing* until *exactly the same thing* until one of its dependent values changes.
one of its dependent values changes.
Dependent values should not change during a single turn of the event loop. Dependent values should not change during a single turn of the event loop.
If an idempotent expression returns a string or a number, it returns the same string or number when called twice in a row. If the expression returns an object, including an `array`, it returns the same object *reference* when called twice in a row. If an idempotent expression returns a string or a number, it returns the same string or number when called twice in a row. If the expression returns an object, including an `array`, it returns the same object *reference* when called twice in a row.
<div class="alert is-helpful"> <div class="alert is-helpful">
There is one exception to this behavior that applies to `*ngFor`. `*ngFor` has `trackBy` functionality that can deal with referential inequality of objects that when iterating over them. There is one exception to this behavior that applies to `*ngFor`. `*ngFor` has `trackBy` functionality that can deal with referential inequality of objects when iterating over them. See [*ngFor with `trackBy`](guide/template-syntax#ngfor-with-trackby) for details.
For more information, see the [*ngFor with `trackBy`](guide/template-syntax#ngfor-with-trackby) section of this guide.
</div> </div>
### Quick execution
Angular executes template expressions after every change detection cycle.
Change detection cycles are triggered by many asynchronous activities such as
promise resolutions, HTTP results, timer events, key presses and mouse moves.
Expressions should finish quickly or the user experience may drag, especially on slower devices.
Consider caching values when their computation is expensive.
### Simplicity
Although it's possible to write complex template expressions, it's a better
practice to avoid them.
A property name or method call should be the norm, but an occasional Boolean negation, `!`, is OK.
Otherwise, confine application and business logic to the component,
where it is easier to develop and test.
<!-- end of Interpolation doc --> <!-- end of Interpolation doc -->
<hr/> <hr/>
@ -278,19 +270,15 @@ Template context names take precedence over component context names.
In `deleteHero(hero)` above, the `hero` is the template input variable, In `deleteHero(hero)` above, the `hero` is the template input variable,
not the component's `hero` property. not the component's `hero` property.
### Statement guidelines
Template statements cannot refer to anything in the global namespace. They Template statements cannot refer to anything in the global namespace. They
can't refer to `window` or `document`. can't refer to `window` or `document`.
They can't call `console.log` or `Math.max`. They can't call `console.log` or `Math.max`.
### Statement guidelines
As with expressions, avoid writing complex template statements. As with expressions, avoid writing complex template statements.
A method call or simple property assignment should be the norm. A method call or simple property assignment should be the norm.
Now that you have a feel for template expressions and statements,
you're ready to learn about the varieties of data binding syntax beyond interpolation.
<hr/> <hr/>
{@a binding-syntax} {@a binding-syntax}
@ -396,7 +384,7 @@ Every public member of a **source** directive is automatically available for bin
You don't have to do anything special to access a directive member in a template expression or statement. You don't have to do anything special to access a directive member in a template expression or statement.
## Data-binding and HTML ### Data-binding and HTML
In the normal course of HTML development, you create a visual structure with HTML elements, and In the normal course of HTML development, you create a visual structure with HTML elements, and
you modify those elements by setting element attributes with string constants. you modify those elements by setting element attributes with string constants.
@ -415,10 +403,10 @@ Notice that the binding is to the `disabled` property of the button's DOM elemen
**not** the attribute. This applies to data-binding in general. Data-binding works with *properties* of DOM elements, components, and directives, not HTML *attributes*. **not** the attribute. This applies to data-binding in general. Data-binding works with *properties* of DOM elements, components, and directives, not HTML *attributes*.
## HTML attribute vs. DOM property ### HTML attribute vs. DOM property
The distinction between an HTML attribute and a DOM property is key to understanding The distinction between an HTML attribute and a DOM property is key to understanding
how Angular binding works. **Attributes are defined by HTML. Properties are accessed from DOM, or the Document Object Model, nodes.** how Angular binding works. **Attributes are defined by HTML. Properties are accessed from DOM (Document Object Model) nodes.**
* A few HTML attributes have 1:1 mapping to properties; for example, `id`. * A few HTML attributes have 1:1 mapping to properties; for example, `id`.
@ -426,31 +414,30 @@ how Angular binding works. **Attributes are defined by HTML. Properties are acce
* Some DOM properties don't have corresponding attributes; for example, `textContent`. * Some DOM properties don't have corresponding attributes; for example, `textContent`.
This general rule can help you build a mental model of attributes and DOM properties: It is important to remember that *HTML attribute* and the *DOM property* are different things, even when they have the same name.
**attributes initialize DOM properties and then they are done. In Angular, the only role of HTML attributes is to initialize element and directive state.
Property values can change; attribute values can't.**
**Template binding works with *properties* and *events*, not *attributes*.**
When you write a data-binding, you're dealing exclusively with the *DOM properties* and *events* of the target object.
<div class="alert is-helpful"> <div class="alert is-helpful">
There is, of course, an exception to this rule because attributes can be changed by `setAttribute()`, which will re-initialize corresponding DOM properties again. This general rule can help you build a mental model of attributes and DOM properties:
**Attributes initialize DOM properties and then they are done.
Property values can change; attribute values can't.**
There is one exception to this rule.
Attributes can be changed by `setAttribute()`, which re-initializes corresponding DOM properties.
</div> </div>
Comparing the [`<td>` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
attributes to the [`<td>` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement)
provides a helpful
example for differentiation. In particular, you can navigate from the attributes
page to the properties via "DOM interface" link, and navigate the inheritance
hierarchy up to `HTMLTableCellElement`.
**The HTML attribute and the DOM property are not the same thing, even when they have the same name.**
For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties. For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties.
Comparing the [`<td>` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) attributes to the [`<td>` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) provides a helpful example for differentiation.
In particular, you can navigate from the attributes page to the properties via "DOM interface" link, and navigate the inheritance hierarchy up to `HTMLTableCellElement`.
#### Example 1: an `<input>`
### Example 1: an `<input>`
When the browser renders `<input type="text" value="Sarah">`, it creates a When the browser renders `<input type="text" value="Sarah">`, it creates a
corresponding DOM node with a `value` property initialized to "Sarah". corresponding DOM node with a `value` property initialized to "Sarah".
@ -466,7 +453,7 @@ The HTML attribute `value` specifies the *initial* value; the DOM `value` proper
To see attributes versus DOM properties in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax. To see attributes versus DOM properties in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax.
### Example 2: a disabled button #### Example 2: a disabled button
The `disabled` attribute is another example. A button's `disabled` The `disabled` attribute is another example. A button's `disabled`
*property* is `false` by default so the button is enabled. *property* is `false` by default so the button is enabled.
@ -479,8 +466,7 @@ so the button is disabled.
<button disabled>Test Button</button> <button disabled>Test Button</button>
``` ```
Adding and removing the `disabled` *attribute* disables and Adding and removing the `disabled` *attribute* disables and enables the button.
enables the button.
However, the value of the *attribute* is irrelevant, However, the value of the *attribute* is irrelevant,
which is why you cannot enable a button by writing `<button disabled="false">Still Disabled</button>`. which is why you cannot enable a button by writing `<button disabled="false">Still Disabled</button>`.
@ -488,7 +474,7 @@ To control the state of the button, set the `disabled` *property*,
<div class="alert is-helpful"> <div class="alert is-helpful">
**Note:** Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following: Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following:
```html ```html
<input [disabled]="condition ? true : false"> <input [disabled]="condition ? true : false">
@ -499,26 +485,15 @@ Generally, use property binding over attribute binding as it is more intuitive (
</div> </div>
**The HTML attribute and the DOM property are different things, even when they have the same name.**
**Template binding works with *properties* and *events*, not *attributes*.**
To see the `disabled` button example in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax. This example shows you how to toggle the disabled property from the component. To see the `disabled` button example in a functioning app, see the <live-example name="binding-syntax"></live-example> especially for binding syntax. This example shows you how to toggle the disabled property from the component.
## Binding types and targets
### Angular and attributes
In Angular, the only role of attributes is to initialize element and directive state.
When you write a data-binding, you're dealing exclusively with properties and events of the target object.
## Binding targets
The **target of a data-binding** is something in the DOM. The **target of a data-binding** is something in the DOM.
Depending on the binding type, the target can be a Depending on the binding type, the target can be a property (element, component, or directive),
property (element, component, or directive), an an event (element, component, or directive), or sometimes an attribute name.
event (element, component, or directive), or sometimes an attribute name. The following table summarizes the targets for the different binding types.
The following table summarizes:
<style> <style>
td, th {vertical-align: top} td, th {vertical-align: top}
@ -678,10 +653,9 @@ for parent and child components to communicate:
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example> <code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
### Binding target ### Binding targets
An element property between enclosing square brackets identifies An element property between enclosing square brackets identifies the target property.
the target property.
The target property in the following code is the image element's `src` property. The target property in the following code is the image element's `src` property.
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example> <code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
@ -847,10 +821,10 @@ property binding but both approaches render the
content harmlessly. The following is the browser output content harmlessly. The following is the browser output
of the `evilTitle` examples. of the `evilTitle` examples.
``` <code-example language="bash">
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title. "Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
"Template alert("evil never sleeps")Syntax" is the property bound evil title. "Template alert("evil never sleeps")Syntax" is the property bound evil title.
``` </code-example>
<hr/> <hr/>
{@a other-bindings} {@a other-bindings}
@ -898,7 +872,7 @@ If you wrote something like this:
You'd get this error: You'd get this error:
<code-example format="nocode"> <code-example language="bash">
Template parse errors: Template parse errors:
Can't bind to 'colspan' since it isn't a known native property Can't bind to 'colspan' since it isn't a known native property
</code-example> </code-example>
@ -968,8 +942,8 @@ The following example conditionally sets the font size in “em” and “%”
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html"></code-example> <code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html"></code-example>
**This technique is suitable for setting a single style, but consider This technique is suitable for setting a single style, but consider
the [`NgStyle`](guide/template-syntax#ngStyle) directive when setting several inline styles at the same time.** the [`NgStyle`](guide/template-syntax#ngStyle) directive when setting several inline styles at the same time.
<div class="alert is-helpful"> <div class="alert is-helpful">
@ -1156,7 +1130,7 @@ Angular desugars the `SizerComponent` binding into this:
The `$event` variable contains the payload of the `SizerComponent.sizeChange` event. The `$event` variable contains the payload of the `SizerComponent.sizeChange` event.
Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the user clicks the buttons. Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the user clicks the buttons.
## Two-way binding in forms ### Two-way binding in forms
The two-way binding syntax is a great convenience compared to The two-way binding syntax is a great convenience compared to
separate property and event bindings. It would be convenient to separate property and event bindings. It would be convenient to
@ -1417,7 +1391,7 @@ efficient alternative to showing/hiding.
<div class="alert is-helpful"> <div class="alert is-helpful">
**Note:** For more information on `NgIf` and `ngIfElse`, see the [API documentation about NgIf](api/common/NgIf). For more information on `NgIf` and `ngIfElse`, see the [API documentation about NgIf](api/common/NgIf).
</div> </div>
@ -1448,26 +1422,20 @@ See also the
`NgFor` is a repeater directive&mdash;a way to present a list of items. `NgFor` is a repeater directive&mdash;a way to present a list of items.
You define a block of HTML that defines how a single item should be displayed You define a block of HTML that defines how a single item should be displayed
and then you tell Angular to use that block as a template for rendering each item in the list. and then you tell Angular to use that block as a template for rendering each item in the list.
The text assigned to `*ngFor` is the instruction that guides the repeater process.
Here is an example of `NgFor` applied to a simple `<div>`: The following example shows `NgFor` applied to a simple `<div>`. (Don't forget the asterisk (`*`) in front of `ngFor`.)
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example> <code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
You can also apply an `NgFor` to a component element, as in this example: You can also apply an `NgFor` to a component element, as in the following example.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html"></code-example> <code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html"></code-example>
<div class="alert is-critical">
Don't forget the asterisk (`*`) in front of `ngFor`.
</div>
The text assigned to `*ngFor` is the instruction that guides the repeater process.
{@a microsyntax} {@a microsyntax}
#### `*ngFor` microsyntax <div class="callout is-critical">
<header>*ngFor microsyntax</header>
The string assigned to `*ngFor` is not a [template expression](guide/template-syntax#template-expressions). Rather, The string assigned to `*ngFor` is not a [template expression](guide/template-syntax#template-expressions). Rather,
it's a *microsyntax*&mdash;a little language of its own that Angular interprets. it's a *microsyntax*&mdash;a little language of its own that Angular interprets.
@ -1479,15 +1447,15 @@ make it available to the templated HTML for each iteration.*
Angular translates this instruction into an `<ng-template>` around the host element, Angular translates this instruction into an `<ng-template>` around the host element,
then uses this template repeatedly to create a new set of elements and bindings for each `item` then uses this template repeatedly to create a new set of elements and bindings for each `item`
in the list. in the list.
For more information about microsyntax, see the [Structural Directives](guide/structural-directives#microsyntax) guide. For more information about microsyntax, see the [Structural Directives](guide/structural-directives#microsyntax) guide.
</div>
{@a template-input-variable} {@a template-input-variable}
{@a template-input-variables} {@a template-input-variables}
#### Template input variables #### Template input variables
The `let` keyword before `item` creates a template input variable called `item`. The `let` keyword before `item` creates a template input variable called `item`.
@ -1930,7 +1898,7 @@ in the child template UI.
Now, in order to see the `@Output()` working, add the following to the parent's template: Now, in order to see the `@Output()` working, add the following to the parent's template:
``` ```html
<ul> <ul>
<li *ngFor="let item of items">{{item}}</li> <li *ngFor="let item of items">{{item}}</li>
</ul> </ul>
@ -1989,7 +1957,7 @@ properties do indeed exist, double check
that your properties are annotated with `@Input()` / `@Output()` or that you've declared that your properties are annotated with `@Input()` / `@Output()` or that you've declared
them in an `inputs`/`outputs` array: them in an `inputs`/`outputs` array:
<code-example language="sh" class="code-shell"> <code-example language="bash">
Uncaught Error: Template parse errors: Uncaught Error: Template parse errors:
Can't bind to 'item' since it isn't a known property of 'app-item-detail' Can't bind to 'item' since it isn't a known property of 'app-item-detail'
</code-example> </code-example>
@ -2067,7 +2035,7 @@ The generated output would look something like this:
<div class="alert is-helpful"> <div class="alert is-helpful">
**Note**: The pipe operator has a higher precedence than the ternary operator (`?:`), The pipe operator has a higher precedence than the ternary operator (`?:`),
which means `a ? b : c | x` is parsed as `a ? b : (c | x)`. which means `a ? b : c | x` is parsed as `a ? b : (c | x)`.
Nevertheless, for a number of reasons, Nevertheless, for a number of reasons,
the pipe operator cannot be used without parentheses in the first and second operands of `?:`. the pipe operator cannot be used without parentheses in the first and second operands of `?:`.
@ -2097,7 +2065,7 @@ Consider the next example, with a `nullItem`.
Since there is no safe navigation operator and `nullItem` is `null`, JavaScript and Angular would throw a `null` reference error and break the rendering process of Angular: Since there is no safe navigation operator and `nullItem` is `null`, JavaScript and Angular would throw a `null` reference error and break the rendering process of Angular:
<code-example format="nocode"> <code-example language="bash">
TypeError: Cannot read property 'name' of null. TypeError: Cannot read property 'name' of null.
</code-example> </code-example>
@ -2151,9 +2119,9 @@ The non-null assertion operator, `!`, is optional with the exception that you mu
### The `$any()` type cast function ### The `$any()` type cast function
Sometimes a binding expression triggers a type error during [AOT compilation](guide/aot-compiler) and it is not possible or difficult Sometimes a binding expression triggers a type error during [AOT compilation](guide/aot-compiler) and it is not possible or difficult to fully specify the type.
to fully specify the type. To silence the error, you can use the `$any()` cast function to cast To silence the error, you can use the `$any()` cast function to cast
the expression to [the `any` type](http://www.typescriptlang.org/docs/handbook/basic-types.html#any) as in the following example: the expression to the [`any` type](http://www.typescriptlang.org/docs/handbook/basic-types.html#any) as in the following example:
<code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html"></code-example> <code-example path="built-in-template-functions/src/app/app.component.html" region="any-type-cast-function-1" header="src/app/app.component.html"></code-example>
@ -2183,7 +2151,7 @@ Refer to the sample code snippet below for a syntax example:
<code-example path="template-syntax/src/app/svg.component.ts" header="src/app/svg.component.ts"></code-example> <code-example path="template-syntax/src/app/svg.component.ts" header="src/app/svg.component.ts"></code-example>
Add the below code to your `svg.component.svg` file: Add the following code to your `svg.component.svg` file:
<code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg"></code-example> <code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg"></code-example>