mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs: make structural directives guide generic (#44895)
tweak the current structural directives guide (currently mainly targeted at the creation of custom structural directives) so that is more generic and a point of reference for structural directives in general this also includes the re-addition of the one-per-element section removed in PR #40015 resolves #44786 PR Close #44895
This commit is contained in:
parent
30427135ce
commit
c8086b7e86
9 changed files with 111 additions and 101 deletions
|
|
@ -27,11 +27,6 @@ describe('Structural Directives', () => {
|
|||
expect(await paragraph.get(0).getText()).not.toContain('I waved');
|
||||
});
|
||||
|
||||
it('should have only one "Hip!" (the other is erased)', async () => {
|
||||
const paragraph = element.all(by.cssContainingText('p', 'Hip!'));
|
||||
expect(await paragraph.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('appUnless should show 3 paragraph (A)s and (B)s at the start', async () => {
|
||||
const paragraph = element.all(by.css('p.unless'));
|
||||
expect(await paragraph.count()).toEqual(3);
|
||||
|
|
|
|||
|
|
@ -110,15 +110,20 @@
|
|||
|
||||
<p class="code"><div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"></p>
|
||||
<!--#docregion inside-ngfor -->
|
||||
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
|
||||
<div
|
||||
*ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById"
|
||||
[class.odd]="odd">
|
||||
({{i}}) {{hero.name}}
|
||||
</div>
|
||||
|
||||
<!--#enddocregion inside-ngfor -->
|
||||
<p class="code"><ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/></p>
|
||||
<!--#docregion inside-ngfor -->
|
||||
<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
|
||||
<div [class.odd]="odd">({{i}}) {{hero.name}}</div>
|
||||
<ng-template ngFor let-hero [ngForOf]="heroes"
|
||||
let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
|
||||
<div [class.odd]="odd">
|
||||
({{i}}) {{hero.name}}
|
||||
</div>
|
||||
</ng-template>
|
||||
<!--#enddocregion inside-ngfor -->
|
||||
|
||||
|
|
@ -162,16 +167,6 @@
|
|||
|
||||
<hr>
|
||||
|
||||
<h2><ng-template></h2>
|
||||
<!-- #docregion template-tag -->
|
||||
<p>Hip!</p>
|
||||
<ng-template>
|
||||
<p>Hip!</p>
|
||||
</ng-template>
|
||||
<p>Hooray!</p>
|
||||
<!-- #enddocregion template-tag -->
|
||||
|
||||
<hr>
|
||||
|
||||
<h2 id="appUnless">UnlessDirective</h2>
|
||||
<!-- #docregion toggle-info -->
|
||||
|
|
|
|||
|
|
@ -251,16 +251,12 @@ then uses this template repeatedly to create a new set of elements and bindings
|
|||
in the list.
|
||||
For more information about shorthand, see the [Structural Directives](guide/structural-directives#shorthand) guide.
|
||||
|
||||
<a id="one-per-element"></a>
|
||||
|
||||
## Repeating elements when a condition is true
|
||||
|
||||
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.
|
||||
|
||||
Because structural directives add and remove nodes from the DOM, apply only one structural directive per element.
|
||||
|
||||
For more information about `NgFor` see the [NgForOf API reference](api/common/NgForOf).
|
||||
For more information see [one structural directive per element](guide/structural-directives#one-per-element).
|
||||
|
||||
<a id="ngfor-with-trackby"></a>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
# Writing structural directives
|
||||
# Structural directives
|
||||
|
||||
This topic demonstrates how to create a structural directive and provides conceptual information on how directives work, how Angular interprets shorthand, and how to add template guard properties to catch template type errors.
|
||||
This guide is about structural directives and provides conceptual information on how such directives work, how Angular interprets their shorthand syntax, and how to add template guard properties to catch template type errors.
|
||||
|
||||
Structural directives are directives which change the DOM layout by adding and removing DOM element.
|
||||
|
||||
Angular provides a set of built-in structural directives (such as `NgIf`, `NgFor`, `NgSwitch` and others) which are commonly used in all Angular projects. For more information see [Built-in directives](guide/built-in-directives).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
|
@ -8,10 +12,81 @@ For the example application that this page describes, see the <live-example name
|
|||
|
||||
</div>
|
||||
|
||||
For more information on Angular's built-in structural directives, such as `NgIf`, `NgForOf`, and `NgSwitch`, see [Built-in directives](guide/built-in-directives).
|
||||
<a id="shorthand"></a>
|
||||
<a id="asterisk"></a>
|
||||
|
||||
## Structural directive shorthand
|
||||
|
||||
When structural directives are applied they generally are prefixed by an asterisk, `*`, such as `*ngIf`. This convention is shorthand that Angular interprets and converts into a longer form.
|
||||
Angular transforms the asterisk in front of a structural directive into an `<ng-template>` that surrounds the host element and its descendants.
|
||||
|
||||
For example, let's take the following code which uses an `*ngIf` to displays the hero's name if `hero` exists:
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (asterisk)" region="asterisk"></code-example>
|
||||
|
||||
Angular creates an `<ng-template>` element and applies the `*ngIf` directive onto it where it becomes a property binding in square brackets, `[ngIf]`. The rest of the `<div>`, including its class attribute, is then moved inside the `<ng-template>`:
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-template)" region="ngif-template"></code-example>
|
||||
|
||||
Note that Angular does not actually create a real `<ng-template>` element, but instead only renders the `<div>` element.
|
||||
|
||||
```html
|
||||
<div _ngcontent-c0>Mr. Nice</div>
|
||||
|
||||
```
|
||||
|
||||
The following example compares the shorthand use of the asterisk in `*ngFor` with the longhand `<ng-template>` form:
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"></code-example>
|
||||
|
||||
Here, everything related to the `ngFor` structural directive is moved to the `<ng-template>`.
|
||||
All other bindings and attributes on the element apply to the `<div>` element within the `<ng-template>`.
|
||||
Other modifiers on the host element, in addition to the `ngFor` string, remain in place as the element moves inside the `<ng-template>`.
|
||||
In this example, the `[class.odd]="odd"` stays on the `<div>`.
|
||||
|
||||
The `let` keyword declares a template input variable that you can reference within the template.
|
||||
The input variables in this example are `hero`, `i`, and `odd`.
|
||||
The parser translates `let hero`, `let i`, and `let odd` into variables named `let-hero`, `let-i`, and `let-odd`.
|
||||
The `let-i` and `let-odd` variables become `let i=index` and `let odd=odd`.
|
||||
Angular sets `i` and `odd` to the current value of the context's `index` and `odd` properties.
|
||||
|
||||
The parser applies PascalCase to all directives and prefixes them with the directive's attribute name, such as ngFor.
|
||||
For example, the `ngFor` input properties, `of` and `trackBy`, map to `ngForOf` and `ngForTrackBy`.
|
||||
|
||||
As the `NgFor` directive loops through the list, it sets and resets properties of its own context object.
|
||||
These properties can include, but aren't limited to, `index`, `odd`, and a special property
|
||||
named `$implicit`.
|
||||
|
||||
Angular sets `let-hero` to the value of the context's `$implicit` property, which `NgFor` has initialized with the hero for the current iteration.
|
||||
|
||||
For more information, see the [NgFor API](api/common/NgForOf "API: NgFor") and [NgForOf API](api/common/NgForOf) documentation.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Note that Angular's `<ng-template>` element defines a template that doesn't render anything by default, if you just wrap elements in an `<ng-template>` without applying a structural directive those elements will not be rendered.
|
||||
|
||||
For more information, see the [ng-template API](api/core/ng-template) documentation.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<a id="one-per-element"></a>
|
||||
## One structural directive per element
|
||||
|
||||
It's a quite common use-case to repeat a block of HTML but only when a particular condition is true. An intuitive way to do that is to put both an `*ngFor` and an `*ngIf` on the same element. However, since both `*ngFor` and `*ngIf` are structural directives, this would be treated as an error by the compiler. You may apply only one _structural_ directive to an element.
|
||||
|
||||
The reason is simplicity. Structural directives can do complex things with the host element and its descendants.
|
||||
|
||||
When two directives lay claim to the same host element, which one should take precedence?
|
||||
|
||||
Which should go first, the `NgIf` or the `NgFor`? Can the `NgIf` cancel the effect of the `NgFor`?
|
||||
If so (and it seems like it should be so), how should Angular generalize the ability to cancel for other structural directives?
|
||||
|
||||
There are no easy answers to these questions. Prohibiting multiple structural directives makes them moot.
|
||||
There's an easy solution for this use case: put the `*ngIf` on a container element that wraps the `*ngFor` element. One or both elements can be an `<ng-container>` so that no extra DOM elements are generated.
|
||||
|
||||
|
||||
<a id="unless"></a>
|
||||
|
||||
## Creating a structural directive
|
||||
|
||||
This section guides you through creating an `UnlessDirective` and how to set `condition` values.
|
||||
|
|
@ -86,76 +161,6 @@ To verify that the directive works, click the button to change the value of `con
|
|||
|
||||
<img alt="UnlessDirective in action" src="generated/images/guide/structural-directives/unless-anim.gif">
|
||||
|
||||
</div>
|
||||
|
||||
<a id="shorthand"></a>
|
||||
<a id="asterisk"></a>
|
||||
|
||||
## Structural directive shorthand
|
||||
|
||||
The asterisk, `*`, syntax on a structural directive, such as `*ngIf`, is shorthand that Angular interprets into a longer form.
|
||||
Angular transforms the asterisk in front of a structural directive into an `<ng-template>` that surrounds the host element and its descendants.
|
||||
|
||||
The following is an example of `*ngIf` that displays the hero's name if `hero` exists:
|
||||
|
||||
<code-example header="src/app/app.component.html (asterisk)" path="structural-directives/src/app/app.component.html" region="asterisk"></code-example>
|
||||
|
||||
The `*ngIf` directive moves to the `<ng-template>` where it becomes a property binding in square brackets, `[ngIf]`.
|
||||
The rest of the `<div>`, including its class attribute, moves inside the `<ng-template>`.
|
||||
|
||||
<code-example header="src/app/app.component.html (ngif-template)" path="structural-directives/src/app/app.component.html" region="ngif-template"></code-example>
|
||||
|
||||
Angular does not create a real `<ng-template>` element, instead rendering only the `<div>` and a comment node placeholder to the DOM.
|
||||
|
||||
<code-example format="html" language="html">
|
||||
|
||||
<!--bindings={
|
||||
"ng-reflect-ng-if": "[object Object]"
|
||||
}-->
|
||||
<div _ngcontent-c0>Mr. Nice</div>
|
||||
|
||||
</code-example>
|
||||
|
||||
The following example compares the shorthand use of the asterisk in `*ngFor` with the longhand `<ng-template>` form:
|
||||
|
||||
<code-example header="src/app/app.component.html (inside-ngfor)" path="structural-directives/src/app/app.component.html" region="inside-ngfor"></code-example>
|
||||
|
||||
Here, everything related to the `ngFor` structural directive applies to the `<ng-template>`.
|
||||
All other bindings and attributes on the element apply to the `<div>` element within the `<ng-template>`.
|
||||
Other modifiers on the host element, in addition to the `ngFor` string, remain in place as the element moves inside the `<ng-template>`.
|
||||
In this example, the `[class.odd]="odd"` stays on the `<div>`.
|
||||
|
||||
The `let` keyword declares a template input variable that you can reference within the template.
|
||||
The input variables in this example are `hero`, `i`, and `odd`.
|
||||
The parser translates `let hero`, `let i`, and `let odd` into variables named `let-hero`, `let-i`, and `let-odd`.
|
||||
The `let-i` and `let-odd` variables become `let i=index` and `let odd=odd`.
|
||||
Angular sets `i` and `odd` to the current value of the context's `index` and `odd` properties.
|
||||
|
||||
The parser applies PascalCase to all directives and prefixes them with the directive's attribute name, such as ngFor.
|
||||
For example, the `ngFor` input properties, `of` and `trackBy`, map to `ngForOf` and `ngForTrackBy`.
|
||||
As the `NgFor` directive loops through the list, it sets and resets properties of its own context object.
|
||||
These properties can include, but aren't limited to, `index`, `odd`, and a special property named `$implicit`.
|
||||
|
||||
Angular sets `let-hero` to the value of the context's `$implicit` property, which `NgFor` has initialized with the hero for the current iteration.
|
||||
|
||||
For more information, see the [NgFor API](api/common/NgForOf "API: NgFor") and [NgForOf API](api/common/NgForOf) documentation.
|
||||
|
||||
### Creating template fragments with `<ng-template>`
|
||||
|
||||
Angular's `<ng-template>` element defines a template that doesn't render anything by default.
|
||||
With `<ng-template>`, you can render the content manually for full control over how the content displays.
|
||||
|
||||
If there is no structural directive and you wrap some elements in an `<ng-template>`, those elements disappear.
|
||||
In the following example, Angular does not render the middle "Hip!" in the phrase "Hip! Hip! Hooray!" because of the surrounding `<ng-template>`.
|
||||
|
||||
<code-example header="src/app/app.component.html (template-tag)" path="structural-directives/src/app/app.component.html" region="template-tag"></code-example>
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
<img alt="template tag rendering" src="generated/images/guide/structural-directives/template-rendering.png">
|
||||
|
||||
</div>
|
||||
|
||||
## Structural directive syntax reference
|
||||
|
||||
When you write your own structural directives, use the following syntax:
|
||||
|
|
@ -281,7 +286,9 @@ The following snippet shows an example of such a function.
|
|||
export class ExampleDirective {
|
||||
// Make sure the template checker knows the type of the context with which the
|
||||
// template of this directive will be rendered
|
||||
static ngTemplateContextGuard(dir: ExampleDirective, ctx: unknown): ctx is ExampleContext { return true; };
|
||||
static ngTemplateContextGuard(
|
||||
dir: ExampleDirective, ctx: unknown
|
||||
): ctx is ExampleContext { return true; };
|
||||
|
||||
// …
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
|
|
@ -67,6 +67,8 @@ As we said that would not work, what we can do is to simply move one of the stru
|
|||
|
||||
This would work as intended without introducing any new unnecessary elements in the DOM.
|
||||
|
||||
For more information see [one structural directive per element](guide/structural-directives#one-per-element).
|
||||
|
||||
### Use alongside ngTemplateOutlet
|
||||
|
||||
The `NgTemplateOutlet` directive can be applied to any element but most of the time it's applied to `<ng-container>` ones. By combining the two, we get a very clear and easy to follow HTML and DOM structure in which no extra elements are necessary and template views are instantiated where requested.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,21 @@ Angular's `<ng-template>` element defines a template that is not rendered by def
|
|||
|
||||
With `<ng-template>`, you can define template content that is only being rendered by Angular when you, whether directly or indirectly, specifically instruct it to do so, allowing you to have full control over how and when the content is displayed.
|
||||
|
||||
<div class="alter is-helpful">
|
||||
|
||||
Note that if you wrap content inside an `<ng-template>` without instructing Angular to render it, such content will not appear on a page. For example, see the following HTML code, when handling it Angular won't render the middle "Hip!" in the phrase "Hip! Hip! Hooray!" because of the surrounding `<ng-template>`.
|
||||
|
||||
```html
|
||||
<p>Hip!</p>
|
||||
<ng-template>
|
||||
<p>Hip!</p>
|
||||
</ng-template>
|
||||
<p>Hooray!</p>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@usageNotes
|
||||
|
||||
### Structural Directives
|
||||
|
|
|
|||
|
|
@ -67,11 +67,11 @@ export class NgForOfContext<T, U extends NgIterable<T> = NgIterable<T>> {
|
|||
* context according to its lexical position.
|
||||
*
|
||||
* When using the shorthand syntax, Angular allows only [one structural directive
|
||||
* on an element](guide/built-in-directives#one-per-element).
|
||||
* on an element](guide/structural-directives#one-per-element).
|
||||
* If you want to iterate conditionally, for example,
|
||||
* put the `*ngIf` on a container element that wraps the `*ngFor` element.
|
||||
* For futher discussion, see
|
||||
* [Structural Directives](guide/built-in-directives#one-per-element).
|
||||
* [Structural Directives](guide/structural-directives#one-per-element).
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
|
|||
*
|
||||
* The presence of the implicit template object has implications for the nesting of
|
||||
* structural directives. For more on this subject, see
|
||||
* [Structural Directives](https://angular.io/guide/built-in-directives#one-per-element).
|
||||
* [Structural Directives](guide/structural-directives#one-per-element).
|
||||
*
|
||||
* @ngModule CommonModule
|
||||
* @publicApi
|
||||
|
|
|
|||
Loading…
Reference in a new issue