docs: edit builtin-directives doc (#39816)

Edit built-in-directives.md copy and headers, fixes affected
links, adds a docregion to make steps clearer.

PR Close #39816
This commit is contained in:
Kapunahele Wong 2020-11-18 08:47:53 -05:00 committed by Joey Perrott
parent 95ad452e54
commit 75db0c7445
10 changed files with 214 additions and 279 deletions

View File

@ -7,11 +7,8 @@
<fieldset><h4>NgModel examples</h4>
<p>Current item name: {{currentItem.name}}</p>
<p>
<!-- #docregion without-NgModel -->
<label for="without">without NgModel:</label>
<input [value]="currentItem.name" (input)="currentItem.name=$event.target.value" id="without">
<!-- #enddocregion without-NgModel -->
</p>
<p>
@ -27,10 +24,8 @@
</p>
<p>
<!-- #docregion NgModelChange -->
<label for="example-change">(ngModelChange)="...name=$event":</label>
<input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">
<!-- #enddocregion NgModelChange -->
</p>
<p>
@ -77,12 +72,9 @@
<!-- NgStyle binding -->
<hr><h3>NgStyle Binding</h3>
<!-- #docregion without-ng-style -->
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
This div is x-large or smaller.
</div>
<!-- #enddocregion without-ng-style -->
<h4>[ngStyle] binding to currentStyles - CSS property names</h4>
<p>currentStyles is {{currentStyles | json}}</p>
@ -134,7 +126,6 @@
<hr>
<h4>Show/hide vs. NgIf</h4>
<!-- #docregion NgIf-3 -->
<!-- isSpecial is true -->
<div [class.hidden]="!isSpecial">Show with class</div>
<div [class.hidden]="isSpecial">Hide with class</div>
@ -144,7 +135,6 @@
<div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 -->
<hr>

View File

@ -21,7 +21,10 @@ export class AppComponent implements OnInit {
item: Item; // defined to demonstrate template context precedence
items: Item[];
// #docregion item
currentItem: Item;
// #enddocregion item
// trackBy change counting

View File

@ -5,9 +5,13 @@ import { Item } from './item';
selector: 'app-stout-item',
template: `I'm a little {{item.name}}, short and stout!`
})
// #docregion input
export class StoutItemComponent {
@Input() item: Item;
}
// #enddocregion input
@Component({
selector: 'app-best-item',

View File

@ -140,8 +140,7 @@ The following table lists some of the key AngularJS template features with their
Angular has true template input variables that are explicitly defined using the `let` keyword.
For more information, see the [ngFor micro-syntax](guide/built-in-directives#microsyntax)
section of the [Built-in Directives](guide/built-in-directives) page.
For more information, see the [Template input variables](guide/structural-directives#template-input-variable) section of [Structural Directives](guide/structural-directives).
</td>
</tr>
@ -641,7 +640,7 @@ The following are some of the key AngularJS built-in directives and their equiva
For more information on style binding, see the [Style binding](guide/attribute-binding#style-binding) section of the
[Attribute binding](guide/attribute-binding) page.
For more information on the `ngStyle` directive, see the [NgStyle](guide/built-in-directives#ngStyle)
For more information on the `ngStyle` directive, see the [NgStyle](guide/built-in-directives#ngstyle)
section of the [Built-in directives](guide/built-in-directives) page.
</td>

View File

@ -181,7 +181,7 @@ The `ngModel` directive, which implements two-way data binding, is an example of
Angular has more pre-defined directives that either alter the layout structure
(for example, [ngSwitch](guide/built-in-directives#ngSwitch))
or modify aspects of DOM elements and components
(for example, [ngStyle](guide/built-in-directives#ngStyle) and [ngClass](guide/built-in-directives#ngClass)).
(for example, [ngStyle](guide/built-in-directives#ngstyle) and [ngClass](guide/built-in-directives#ngClass)).
<div class="alert is-helpful">

View File

@ -22,7 +22,7 @@ Two examples are [NgFor](guide/built-in-directives#ngFor) and [NgIf](guide/built
Learn about them in the [Structural Directives](guide/structural-directives) guide.
*Attribute directives* are used as attributes of elements.
The built-in [NgStyle](guide/built-in-directives#ngStyle) directive in the
The built-in [NgStyle](guide/built-in-directives#ngstyle) directive in the
[Built-in directives](guide/built-in-directives) guide, for example,
can change several element styles at the same time.

View File

@ -1,6 +1,8 @@
# Built-in directives
Angular offers two kinds of built-in directives: [_attribute_ directives](guide/attribute-directives) and [_structural_ directives](guide/structural-directives).
Directives are classes that add additional behavior to elements
in your Angular applications.
With Angular's built-in directives, you can manage forms, lists, styles, and what users see.
<div class="alert is-helpful">
@ -8,29 +10,31 @@ See the <live-example></live-example> for a working example containing the code
</div>
For more detail, including how to build your own custom directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives).
The different types of Angular directives are as follows:
1. [Components](guide/component-overview)&mdash;directives with a template.
This type of directive is the most common directive type.
1. [Attribute directives](guide/built-in-directives#built-in-attribute-directives)&mdash;directives that change the appearance or behavior of an element, component, or another directive.
1. [Structural directives](guide/built-in-directives#built-in-structural-directives)&mdash;directives that change the DOM layout by adding and removing DOM elements.
This guide covers built-in [attribute directives](guide/built-in-directives#built-in-attribute-directives) and [structural directives](guide/built-in-directives#built-in-structural-directives).
{@a attribute-directives}
## Built-in attribute directives
Attribute directives listen to and modify the behavior of
other HTML elements, attributes, properties, and components.
You usually apply them to elements as if they were HTML attributes, hence the name.
Attribute directives listen to and modify the behavior of other HTML elements, attributes, properties, and components.
Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation")
and the [`FormsModule`](guide/forms "Forms") define their own attribute directives.
Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation") and the [`FormsModule`](guide/forms "Forms") define their own attribute directives.
The most common attribute directives are as follows:
* [`NgClass`](guide/built-in-directives#ngClass)&mdash;adds and removes a set of CSS classes.
* [`NgStyle`](guide/built-in-directives#ngStyle)&mdash;adds and removes a set of HTML styles.
* [`NgStyle`](guide/built-in-directives#ngstyle)&mdash;adds and removes a set of HTML styles.
* [`NgModel`](guide/built-in-directives#ngModel)&mdash;adds two-way data binding to an HTML form element.
{@a ngClass}
## `NgClass`
## Adding and removing classes with `NgClass`
Add or remove several CSS classes simultaneously with `ngClass`.
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html"></code-example>
You can add or remove multiple CSS classes simultaneously with `ngClass`.
<div class="alert is-helpful">
@ -38,114 +42,72 @@ To add or remove a *single* class, use [class binding](guide/attribute-binding#c
</div>
Consider a `setCurrentClasses()` component method that sets a component property,
`currentClasses`, with an object that adds or removes three classes based on the
`true`/`false` state of three other component properties. Each key of the object is a CSS class name; its value is `true` if the class should be added,
`false` if it should be removed.
### Using `NgClass` with an expression
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts"></code-example>
On the element you'd like to style, add `[ngClass]` and set it equal to an expression.
In this case, `isSpecial` is a boolean set to `true` in `app.component.ts`.
Because `isSpecial` is true, `ngClass` applies the class of `special` to the `<div>`.
Adding an `ngClass` property binding to `currentClasses` sets the element's classes accordingly:
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html"></code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html"></code-example>
### Using `NgClass` with a method
<div class="alert is-helpful">
1. To use `NgClass` with a method, add the method to the component class.
In the following example, `setCurrentClasses()` sets the property `currentClasses` with an object that adds or removes three classes based on the `true` or `false` state of three other component properties.
Remember that in this situation you'd call `setCurrentClasses()`,
both initially and when the dependent properties change.
Each key of the object is a CSS class name.
If a key is `true`, `ngClass` adds the class.
If a key is `false`, `ngClass` removes the class.
</div>
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts"></code-example>
{@a ngStyle}
## `NgStyle`
1. In the template, add the `ngClass` property binding to `currentClasses` to set the element's classes:
Use `NgStyle` to set many inline styles simultaneously and dynamically, based on the state of the component.
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html"></code-example>
### Without `NgStyle`
For this use case, Angular applies the classes on initialization and in case of changes.
The full example calls `setCurrentClasses()` initially with `ngOnInit()` and when the dependent properties change through a button click.
These steps are not necessary to implement `ngClass`.
For more information, see the <live-example></live-example> `app.component.ts` and `app.component.html`.
For context, consider setting a *single* style value with [style binding](guide/attribute-binding#style-binding), without `NgStyle`.
{@a ngstyle}
## Setting inline styles with `NgStyle`
<code-example path="built-in-directives/src/app/app.component.html" region="without-ng-style" header="src/app/app.component.html"></code-example>
You can use `NgStyle` to set multiple inline styles simultaneously, based on the state of the component.
However, to set *many* inline styles at the same time, use the `NgStyle` directive.
1. To use `NgStyle`, add a method to the component class.
The following is a `setCurrentStyles()` method that sets a component
property, `currentStyles`, with an object that defines three styles,
based on the state of three other component properties:
In the following example, `setCurrentStyles()` sets the property `currentStyles` with an object that defines three styles, based on the state of three other component properties.
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts"></code-example>
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts"></code-example>
Adding an `ngStyle` property binding to `currentStyles` sets the element's styles accordingly:
1. To set the element's styles, add an `ngStyle` property binding to `currentStyles`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
Remember to call `setCurrentStyles()`, both initially and when the dependent properties change.
</div>
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html"></code-example>
For this use case, Angular applies the styles upon initialization and in case of changes.
To do this, the full example calls `setCurrentStyles()` initially with `ngOnInit()` and when the dependent properties change through a button click.
However, these steps are not necessary to implement `ngStyle` on its own.
See the <live-example></live-example> `app.component.ts` and `app.component.html` for this optional implementation.
{@a ngModel}
## `[(ngModel)]`: Two-way binding
## Displaying and updating properties with `ngModel`
The `NgModel` directive allows you to display a data property and
update that property when the user makes changes. Here's an example:
You can use the `NgModel` directive to display a data property and update that property when the user makes changes.
<code-example path="built-in-directives/src/app/app.component.html" header="src/app/app.component.html (NgModel example)" region="NgModel-1"></code-example>
1. Import `FormsModule` and add it to the NgModule's `imports` list.
<code-example path="built-in-directives/src/app/app.module.ts" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module"></code-example>
### Import `FormsModule` to use `ngModel`
1. Add an `[(ngModel)]` binding on an HTML `<form>` element and set it equal to the property, here `name`.
Before using the `ngModel` directive in a two-way data binding,
you must import the `FormsModule` and add it to the NgModule's `imports` list.
Learn more about the `FormsModule` and `ngModel` in [Forms](guide/forms#ngModel).
<code-example path="built-in-directives/src/app/app.component.html" header="src/app/app.component.html (NgModel example)" region="NgModel-1"></code-example>
Remember to import the `FormsModule` to make `[(ngModel)]` available as follows:
This `[(ngModel)]` syntax can only set a data-bound property.
<code-example path="built-in-directives/src/app/app.module.ts" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module"></code-example>
You could achieve the same result with separate bindings to
the `<input>` element's `value` property and `input` event:
<code-example path="built-in-directives/src/app/app.component.html" region="without-NgModel" header="src/app/app.component.html"></code-example>
To streamline the syntax, the `ngModel` directive hides the details behind its own `ngModel` input and `ngModelChange` output properties:
<code-example path="built-in-directives/src/app/app.component.html" region="NgModelChange" header="src/app/app.component.html"></code-example>
The `ngModel` data property sets the element's value property and the `ngModelChange` event property
listens for changes to the element's value.
### `NgModel` and value accessors
The details are specific to each kind of element and therefore the `NgModel` directive only works for an element
supported by a [ControlValueAccessor](api/forms/ControlValueAccessor)
that adapts an element to this protocol.
Angular provides *value accessors* for all of the basic HTML form elements and the
[Forms](guide/forms) guide shows how to bind to them.
You can't apply `[(ngModel)]` to a non-form native element or a
third-party custom component until you write a suitable value accessor. For more information, see
the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor).
You don't need a value accessor for an Angular component that
you write because you can name the value and event properties
to suit Angular's basic [two-way binding syntax](guide/two-way-binding)
and skip `NgModel` altogether.
The `sizer` in the
[Two-way Binding](guide/two-way-binding) section is an example of this technique.
Separate `ngModel` bindings are an improvement over binding to the
element's native properties, but you can streamline the binding with a
single declaration using the `[(ngModel)]` syntax:
<code-example path="built-in-directives/src/app/app.component.html" region="NgModel-1" header="src/app/app.component.html"></code-example>
This `[(ngModel)]` syntax can only _set_ a data-bound property.
If you need to do something more, you can write the expanded form;
for example, the following changes the `<input>` value to uppercase:
To customize your configuration, you can write the expanded form, which separates the property and event binding.
Use [property binding](guide/property-binding) to set the property and [event binding](guide/event-binding) to respond to changes.
The following example changes the `<input>` value to uppercase:
<code-example path="built-in-directives/src/app/app.component.html" region="uppercase" header="src/app/app.component.html"></code-example>
@ -155,198 +117,146 @@ Here are all variations in action, including the uppercase version:
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
</div>
### `NgModel` and value accessors
The `NgModel` directive works for an element supported by a [ControlValueAccessor](api/forms/ControlValueAccessor).
Angular provides *value accessors* for all of the basic HTML form elements.
For more information, see [Forms](guide/forms).
To apply `[(ngModel)]` to a non-form native element or a third-party custom component, you have to write a value accessor.
For more information, see the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor).
<div class="alert is-helpful">
When you write an Angular component, you don't need a value accessor or `NgModel` if you name the value and event properties according to Angular's [two-way binding syntax](guide/two-way-binding#how-two-way-binding-works).
</div>
{@a structural-directives}
## Built-in _structural_ directives
## Built-in structural directives
Structural directives are responsible for HTML layout.
They shape or reshape the DOM's structure, typically by adding, removing, and manipulating
the host elements to which they are attached.
They shape or reshape the DOM's structure, typically by adding, removing, and manipulating the host elements to which they are attached.
This section is an introduction to the common built-in structural directives:
This section introduces the most common built-in structural directives:
* [`NgIf`](guide/built-in-directives#ngIf)&mdash;conditionally creates or destroys subviews from the template.
* [`NgIf`](guide/built-in-directives#ngIf)&mdash;conditionally creates or disposes of subviews from the template.
* [`NgFor`](guide/built-in-directives#ngFor)&mdash;repeat a node for each item in a list.
* [`NgSwitch`](guide/built-in-directives#ngSwitch)&mdash;a set of directives that switch among alternative views.
<div class="alert is-helpful">
For more information, see [Structural Directives](guide/structural-directives).
The deep details of structural directives are covered in the
[Structural Directives](guide/structural-directives) guide,
which explains the following:
* Why you
[prefix the directive name with an asterisk (\*)](guide/structural-directives#the-asterisk--prefix).
* Using [`<ng-container>`](guide/structural-directives#ngcontainer "<ng-container>")
to group elements when there is no suitable host element for the directive.
* How to write your own structural directive.
* Why you [can only apply one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
</div>
{@a ngIf}
## NgIf
## Adding or removing an element with `NgIf`
You can add or remove an element from the DOM by applying an `NgIf` directive to
a host element.
Bind the directive to a condition expression like `isActive` in this example.
You can add or remove an element by applying an `NgIf` directive to a host element.
When `NgIf` is `false`, Angular removes an element and its descendants from the DOM.
Angular then disposes of their components, which frees up memory and resources.
To add or remove an element, bind `*ngIf` to a condition expression such as `isActive` in the following example.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-1" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
When the `isActive` expression returns a truthy value, `NgIf` adds the `ItemDetailComponent` to the DOM.
When the expression is falsy, `NgIf` removes the `ItemDetailComponent` from the DOM and disposes of the component and all of its sub-components.
Don't forget the asterisk (`*`) in front of `ngIf`. For more information
on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of
[Structural Directives](guide/structural-directives).
For more information on `NgIf` and `NgIfElse`, see the [NgIf API documentation](api/common/NgIf).
</div>
### Guarding against `null`
When the `isActive` expression returns a truthy value, `NgIf` adds the
`ItemDetailComponent` to the DOM.
When the expression is falsy, `NgIf` removes the `ItemDetailComponent`
from the DOM, destroying that component and all of its sub-components.
By default, `NgIf` prevents display of an element bound to a null value.
### Show/hide vs. `NgIf`
Hiding an element is different from removing it with `NgIf`.
For comparison, the following example shows how to control
the visibility of an element with a
[class](guide/attribute-binding#class-binding) or [style](guide/attribute-binding#style-binding) binding.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-3" header="src/app/app.component.html"></code-example>
When you hide an element, that element and all of its descendants remain in the DOM.
All components for those elements stay in memory and
Angular may continue to check for changes.
You could be holding onto considerable computing resources and degrading performance
unnecessarily.
`NgIf` works differently. When `NgIf` is `false`, Angular removes the element and its descendants from the DOM.
It destroys their components, freeing up resources, which
results in a better user experience.
If you are hiding large component trees, consider `NgIf` as a more
efficient alternative to showing/hiding.
<div class="alert is-helpful">
For more information on `NgIf` and `ngIfElse`, see the [API documentation about NgIf](api/common/NgIf).
</div>
### Guard against null
Another advantage of `ngIf` is that you can use it to guard against null. Show/hide
is best suited for very simple use cases, so when you need a guard, opt instead for `ngIf`. Angular will throw an error if a nested expression tries to access a property of `null`.
The following shows `NgIf` guarding two `<div>`s.
The `currentCustomer` name appears only when there is a `currentCustomer`.
The `nullCustomer` will not be displayed as long as it is `null`.
To use `NgIf` to guard a `<div>`, add `*ngIf="yourProperty"` to the `<div>`.
In the following example, the `currentCustomer` name appears because there is a `currentCustomer`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2" header="src/app/app.component.html"></code-example>
However, if the property is `null`, Angular does not display the `<div>`.
In this example, Angular does not display the `nullCustomer` because it is `null`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2b" header="src/app/app.component.html"></code-example>
{@a ngFor}
## `NgFor`
## Listing items with `NgFor`
`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
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.
You can use the `NgFor` directive to present a list of items.
The following example shows `NgFor` applied to a simple `<div>`.
1. Define a block of HTML that determines how Angular renders a single item.
1. To list your items, assign the short hand `let item of items` to `*ngFor`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
The string `"let item of items"` instructs Angular to do the following:
Don't forget the asterisk (`*`) in front of `ngFor`. For more information
on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of
[Structural Directives](guide/structural-directives).
* Store each item in the `items` array in the local `item` looping variable
* Make each item available to the templated HTML for each iteration
* Translate `"let item of items"` into an `<ng-template>` around the host element
* Repeat the `<ng-template>` for each `item` in the list
</div>
For more information see the [`<ng-template>` section](guide/structural-directives#the-ng-template) of [Structural directives](guide/structural-directives).
### Repeating a component view
You can also apply an `NgFor` to a component element, as in the following example.
To repeat a component element, apply `*ngFor` to the selector.
In the following example, the selector is `<app-item-detail>`.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html"></code-example>
{@a microsyntax}
You can reference a template input variable, such as `item`, in the following locations:
<div class="callout is-critical">
<header>*ngFor microsyntax</header>
* within the `ngFor` host element
* within the host element descendants to access the item's properties
The string assigned to `*ngFor` is not a [template expression](guide/interpolation). Rather,
it's a *microsyntax*&mdash;a little language of its own that Angular interprets.
The string `"let item of items"` means:
> *Take each item in the `items` array, store it in the local `item` looping variable, and
make it available to the templated HTML for each iteration.*
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`
in the list.
For more information about microsyntax, see the [Structural Directives](guide/structural-directives#microsyntax) guide.
</div>
{@a template-input-variable}
{@a template-input-variables}
### Template input variables
The `let` keyword before `item` creates a template input variable called `item`.
The `ngFor` directive iterates over the `items` array returned by the parent component's `items` property
and sets `item` to the current item from the array during each iteration.
Reference `item` within the `ngFor` host element
as well as within its descendants to access the item's properties.
The following example references `item` first in an interpolation
and then passes in a binding to the `item` property of the `<app-item-detail>` component.
The following example references `item` first in an interpolation and then passes in a binding to the `item` property of the `<app-item-detail>` component.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1-2" header="src/app/app.component.html"></code-example>
For more information about template input variables, see
[Structural Directives](guide/structural-directives#template-input-variable).
For more information about template input variables, see [Structural Directives](guide/structural-directives#template-input-variable).
### `*ngFor` with `index`
### Getting the `index` of `*ngFor`
The `index` property of the `NgFor` directive context
returns the zero-based index of the item in each iteration.
You can capture the `index` in a template input variable and use it in the template.
You can get the `index` of `*ngFor` in a template input variable and use it in the template.
The next example captures the `index` in a variable named `i` and displays it with the item name.
In the `*ngFor`, add a semicolon and `let i=index` to the short hand.
The following example gets the `index` in a variable named `i` and displays it with the item name.
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-3" header="src/app/app.component.html"></code-example>
<div class="alert is-helpful">
The index property of the `NgFor` directive context returns the zero-based index of the item in each iteration.
`NgFor` is implemented by the `NgForOf` directive. Read more about the other `NgForOf` context values such as `last`, `even`,
and `odd` in the [NgForOf API reference](api/common/NgForOf).
{@a one-per-element}
</div>
## Repeating elements when a condition is true
{@a trackBy}
### *ngFor with `trackBy`
To repeat a block of HTML when a particular condition is true, put the `*ngIf` on a container element that wraps an `*ngFor` element.
One or both elements can be an `<ng-container>` so you don't have to introduce extra levels of HTML.
If you use `NgFor` with large lists, a small change to one item, such as removing or adding an item, can trigger a cascade of DOM manipulations. For example, re-querying the server could reset a list with all new item objects, even when those items were previously displayed. In this case, Angular sees only a fresh list of new object references and has no choice but to replace the old DOM elements with all new DOM elements.
Because structural directives add and remove nodes from the DOM, apply only one structural directive per element.
You can make this more efficient with `trackBy`.
Add a method to the component that returns the value `NgFor` should track.
In this case, that value is the hero's `id`. If the `id` has already been rendered,
Angular keeps track of it and doesn't re-query the server for the same `id`.
For more information about `NgFor` see the [NgForOf API reference](api/common/NgForOf).
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts"></code-example>
{@a ngfor-with-trackby}
### Tracking items with `*ngFor` `trackBy`
In the microsyntax expression, set `trackBy` to the `trackByItems()` method.
By tracking changes to an item list, you can reduce the number of calls your application makes to the server.
With the `*ngFor` `trackBy` property, Angular can change and re-render only those items that have changed, rather than reloading the entire list of items.
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html"></code-example>
1. Add a method to the component that returns the value `NgFor` should track.
In this example, the value to track is the item's `id`.
If the browser has already rendered `id`, Angular keeps track of it and doesn't re-query the server for the same `id`.
Here is an illustration of the `trackBy` effect.
"Reset items" creates new items with the same `item.id`s.
"Change ids" creates new items with new `item.id`s.
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts"></code-example>
1. In the short hand expression, set `trackBy` to the `trackByItems()` method.
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html"></code-example>
**Change ids** creates new items with new `item.id`s.
In the following illustration of the `trackBy` effect, **Reset items** creates new items with the same `item.id`s.
* With no `trackBy`, both buttons trigger complete DOM element replacement.
* With `trackBy`, only changing the `id` triggers element replacement.
@ -355,53 +265,82 @@ Here is an illustration of the `trackBy` effect.
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
</div>
<div class="alert is-helpful">
Built-in directives use only public APIs; that is,
they do not have special access to any private APIs that other directives can't access.
Built-in directives use only public APIs.
They do not have special access to any private APIs that other directives can't access.
</div>
{@a ngSwitch}
## The `NgSwitch` directives
{@a ngcontainer}
NgSwitch is like the JavaScript `switch` statement.
It displays one element from among several possible elements, based on a switch condition.
Angular puts only the selected element into the DOM.
<!-- API Flagged -->
`NgSwitch` is actually a set of three, cooperating directives:
`NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault` as in the following example.
## Hosting a directive without a DOM element
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.
You can use [`<ng-container>`](guide/structural-directives#ngcontainer) when there's no single element to host the directive.
Here's a conditional paragraph using `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"></code-example>
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
<div class="lightbox">
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
</div>
`NgSwitch` is the controller directive. Bind it to an expression that returns
the *switch value*, such as `feature`. Though the `feature` value in this
example is a string, the switch value can be of any type.
1. Import the `ngModel` directive from `FormsModule`.
**Bind to `[ngSwitch]`**. You'll get an error if you try to set `*ngSwitch` because
`NgSwitch` is an *attribute* directive, not a *structural* directive.
Rather than touching the DOM directly, it changes the behavior of its companion directives.
1. Add `FormsModule` to the imports section of the relevant Angular module.
**Bind to `*ngSwitchCase` and `*ngSwitchDefault`**.
The `NgSwitchCase` and `NgSwitchDefault` directives are _structural_ directives
because they add or remove elements from the DOM.
1. To conditionally exclude an `<option>`, wrap the `<option>` in an `<ng-container>`.
* `NgSwitchCase` adds its element to the DOM when its bound value equals the switch value and removes
its bound value when it doesn't equal the switch value.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"></code-example>
* `NgSwitchDefault` adds its element to the DOM when there is no selected `NgSwitchCase`.
<div class="lightbox">
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
</div>
The switch directives are particularly useful for adding and removing *component elements*.
This example switches among four `item` components defined in the `item-switch.components.ts` file.
Each component has an `item` [input property](guide/inputs-outputs#input "Input property")
which is bound to the `currentItem` of the parent component.
{@a ngSwitch}
## Switching cases with `NgSwitch`
Switch directives work as well with native elements and web components too.
For example, you could replace the `<app-best-item>` switch case with the following.
Like the JavaScript `switch` statement, `NgSwitch` displays one element from among several possible elements, based on a switch condition.
Angular puts only the selected element into the DOM.
<!-- API Flagged -->
`NgSwitch` is a set of three directives:
* `NgSwitch`&mdash;an attribute directive that changes the behavior of its companion directives.
* `NgSwitchCase`&mdash;structural directive that adds its element to the DOM when its bound value equals the switch value and removes its bound value when it doesn't equal the switch value.
* `NgSwitchDefault`&mdash;structural directive that adds its element to the DOM when there is no selected `NgSwitchCase`.
1. On an element, such as a `<div>`, add `[ngSwitch]` bound to an expression that returns the switch value, such as `feature`.
Though the `feature` value in this example is a string, the switch value can be of any type.
1. Bind to `*ngSwitchCase` and `*ngSwitchDefault` on the elements for the cases.
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
1. In the parent component, define `currentItem` so you can use it in the `[ngSwitch]` expression.
<code-example path="built-in-directives/src/app/app.component.ts" region="item" header="src/app/app.component.ts"></code-example>
1. In each child component, add an `item` [input property](guide/inputs-outputs#input "Input property") which is bound to the `currentItem` of the parent component.
The following two snippets show the parent component and one of the child components.
The other child components are identical to `StoutItemComponent`.
<code-example path="built-in-directives/src/app/item-switch.component.ts" region="input" header="In each child component, here StoutItemComponent"></code-example>
<div class="lightbox">
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
</div>
Switch directives also work with native HTML elements and web components.
For example, you could replace the `<app-best-item>` switch case with a `<div>` as follows.
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch-div" header="src/app/app.component.html"></code-example>
<hr />
## What's next
For information on how to build your own custom directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives).

View File

@ -86,7 +86,7 @@ In the following snippet, the expression `recommended` and the expression `itemI
<code-example path="interpolation/src/app/app.component.html" region="component-context" header="src/app/app.component.html"></code-example>
An expression can also refer to properties of the _template's_ context such as a [template input variable](guide/built-in-directives#template-input-variables) or a [template reference variable](guide/template-reference-variables).
An expression can also refer to properties of the _template's_ context such as a [template input variable](guide/structural-directives#template-input-variables) or a [template reference variable](guide/template-reference-variables).
The following example uses a template input variable of `customer`.

View File

@ -96,7 +96,7 @@ Technically it's a directive with a template.
An [*attribute* directive](guide/attribute-directives) changes the appearance or behavior
of an element, component, or another directive.
For example, the built-in [`NgStyle`](guide/built-in-directives#ngStyle) directive
For example, the built-in [`NgStyle`](guide/built-in-directives#ngstyle) directive
changes several element styles at the same time.
You can apply many _attribute_ directives to one host element.

View File

@ -50,7 +50,7 @@ For example, `deleteHero()` of `(click)="deleteHero()"` is a method of the compo
The statement context may also refer to properties of the template's own context.
In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument.
On the next two lines, the `deleteHero()` method takes a [template input variable](guide/built-in-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`.
On the next two lines, the `deleteHero()` method takes a [template input variable](guide/structural-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`.
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>