diff --git a/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts b/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts index 17d60797e48..e053a7ca473 100644 --- a/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts @@ -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); diff --git a/aio/content/examples/structural-directives/src/app/app.component.html b/aio/content/examples/structural-directives/src/app/app.component.html index ea3f06fba50..3f37e67586a 100644 --- a/aio/content/examples/structural-directives/src/app/app.component.html +++ b/aio/content/examples/structural-directives/src/app/app.component.html @@ -110,15 +110,20 @@

<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">

-
+
({{i}}) {{hero.name}}

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"/>

- -
({{i}}) {{hero.name}}
+ +
+ ({{i}}) {{hero.name}} +
@@ -162,16 +167,6 @@
-

<ng-template>

- -

Hip!

- -

Hip!

-
-

Hooray!

- - -

UnlessDirective

diff --git a/aio/content/guide/built-in-directives.md b/aio/content/guide/built-in-directives.md index ddb83fd0d79..4ebb565b23b 100644 --- a/aio/content/guide/built-in-directives.md +++ b/aio/content/guide/built-in-directives.md @@ -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. - ## 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 `` 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). diff --git a/aio/content/guide/structural-directives.md b/aio/content/guide/structural-directives.md index 56f87adaa76..d057a41cbc0 100644 --- a/aio/content/guide/structural-directives.md +++ b/aio/content/guide/structural-directives.md @@ -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).
@@ -8,10 +12,81 @@ For the example application that this page describes, see the -For more information on Angular's built-in structural directives, such as `NgIf`, `NgForOf`, and `NgSwitch`, see [Built-in directives](guide/built-in-directives). + + + +## 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 `` 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: + + + +Angular creates an `` element and applies the `*ngIf` directive onto it where it becomes a property binding in square brackets, `[ngIf]`. The rest of the `
`, including its class attribute, is then moved inside the ``: + + + +Note that Angular does not actually create a real `` element, but instead only renders the `
` element. + +```html +
Mr. Nice
+ +``` + +The following example compares the shorthand use of the asterisk in `*ngFor` with the longhand `` form: + + + +Here, everything related to the `ngFor` structural directive is moved to the ``. +All other bindings and attributes on the element apply to the `
` element within the ``. +Other modifiers on the host element, in addition to the `ngFor` string, remain in place as the element moves inside the ``. +In this example, the `[class.odd]="odd"` stays on the `
`. + +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. + +
+ + Note that Angular's `` element defines a template that doesn't render anything by default, if you just wrap elements in an `` without applying a structural directive those elements will not be rendered. + + For more information, see the [ng-template API](api/core/ng-template) documentation. + +
+ + + +## 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 `` so that no extra DOM elements are generated. + - ## 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 UnlessDirective in action -
- - - - -## 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 `` that surrounds the host element and its descendants. - -The following is an example of `*ngIf` that displays the hero's name if `hero` exists: - - - -The `*ngIf` directive moves to the `` where it becomes a property binding in square brackets, `[ngIf]`. -The rest of the `
`, including its class attribute, moves inside the ``. - - - -Angular does not create a real `` element, instead rendering only the `
` and a comment node placeholder to the DOM. - - - -<!--bindings={ - "ng-reflect-ng-if": "[object Object]" -}--> -<div _ngcontent-c0>Mr. Nice</div> - - - -The following example compares the shorthand use of the asterisk in `*ngFor` with the longhand `` form: - - - -Here, everything related to the `ngFor` structural directive applies to the ``. -All other bindings and attributes on the element apply to the `
` element within the ``. -Other modifiers on the host element, in addition to the `ngFor` string, remain in place as the element moves inside the ``. -In this example, the `[class.odd]="odd"` stays on the `
`. - -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 `` - -Angular's `` element defines a template that doesn't render anything by default. -With ``, 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 ``, those elements disappear. -In the following example, Angular does not render the middle "Hip!" in the phrase "Hip! Hip! Hooray!" because of the surrounding ``. - - - - - ## 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; }; // … } diff --git a/aio/content/images/guide/structural-directives/template-rendering.png b/aio/content/images/guide/structural-directives/template-rendering.png deleted file mode 100644 index 200174bcb00..00000000000 Binary files a/aio/content/images/guide/structural-directives/template-rendering.png and /dev/null differ diff --git a/aio/content/special-elements/core/ng-container.md b/aio/content/special-elements/core/ng-container.md index 8a347cdf164..7cfac4a50ef 100644 --- a/aio/content/special-elements/core/ng-container.md +++ b/aio/content/special-elements/core/ng-container.md @@ -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 `` 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. diff --git a/aio/content/special-elements/core/ng-template.md b/aio/content/special-elements/core/ng-template.md index 389760050b6..7c28fed5df4 100644 --- a/aio/content/special-elements/core/ng-template.md +++ b/aio/content/special-elements/core/ng-template.md @@ -2,6 +2,21 @@ Angular's `` element defines a template that is not rendered by def With ``, 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. +
+ + Note that if you wrap content inside an `` 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 ``. + + ```html +

Hip!

+ +

Hip!

+
+

Hooray!

+ ``` + +
+ + @usageNotes ### Structural Directives diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index 96f35381af4..32bbb2cb210 100644 --- a/packages/common/src/directives/ng_for_of.ts +++ b/packages/common/src/directives/ng_for_of.ts @@ -67,11 +67,11 @@ export class NgForOfContext = NgIterable> { * 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 * diff --git a/packages/common/src/directives/ng_if.ts b/packages/common/src/directives/ng_if.ts index b9f22e960ca..fec23963eee 100644 --- a/packages/common/src/directives/ng_if.ts +++ b/packages/common/src/directives/ng_if.ts @@ -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