1
1
block includes
2
2
include ../_util-fns
3
3
4
+ // The docs standard h4 style uppercases, making code terms unreadable. Override it.
5
+ style .
6
+ h4 {font-size : 17px !important ; text-transform : none !important ;}
7
+ .syntax { font-family : Consolas, ' Lucida Sans' , Courier , sans-serif ; color : black ; font-size : 85% ; }
8
+
4
9
:marked
5
- One of the defining features of a single page application is its manipulation
6
- of the DOM tree. Instead of serving a whole new page every time a user
7
- navigates, whole sections of the DOM appear and disappear according
8
- to the application state. In this chapter we'll look at how Angular
9
- manipulates the DOM and how we can do it ourselves in our own directives.
10
+ This guide looks at how Angular manipulates the DOM with **structural directives** and
11
+ how you can write your own structural directives to do the same thing.
12
+
13
+ ### Table of contents
10
14
11
- In this chapter we will
12
- - [learn what structural directives are](#definition)
13
- - [study *ngIf*](#ngIf)
14
- - [discover the <template> element](#template)
15
- - [understand the asterisk (\*) in **ngFor*](#asterisk)
16
- - [write our own structural directive](#unless)
15
+ - [What structural directives are](#definition)
16
+ - [*NgIf* case study](#ngIf)
17
+ - [The <template> element](#template)
18
+ - [The asterisk (\*) prefix](#asterisk)
19
+ - [Write a structural directive](#unless)
17
20
18
21
Try the <live-example></live-example>.
19
22
@@ -22,25 +25,28 @@ block includes
22
25
:marked
23
26
## What are structural directives?
24
27
25
- There are three kinds of Angular directives:
26
- 1. Components
27
- 1. Attribute directives
28
- 1. Structural directives
29
-
30
- The *Component* is really a directive with a template.
31
- It's the most common of the three directives and we write lots of them as we build our application.
28
+ Structural directives are responsible for HTML layout.
29
+ They shape or reshape the DOM's _structure_, typically by adding, removing, or manipulating
30
+ elements and their children.
32
31
33
- The [*Attribute* directive](attribute-directives.html) changes the appearance or behavior of an element.
34
- The built-in [NgStyle](template-syntax.html#ngStyle) directive, for example,
35
- can change several element styles at the same time.
36
- We can use it to render text bold, italic, and lime green by binding to a
37
- component property that requests such a sickening result.
32
+ Three of the common, built-in structural directives —
33
+ [ngIf](template-syntax.html#ngIf), [ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor) —
34
+ are described in the [_Template Syntax_](template-syntax.html) guide and seen in samples throughout the Angular documentation.
38
35
39
- A *Structural* directive changes the DOM layout by adding and removing DOM elements.
40
- We've seen three of the built-in structural directives in other chapters: [ngIf](template-syntax.html#ngIf),
41
- [ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor).
36
+ Here are examples of all three:
42
37
43
38
+ makeExample('structural-directives/ts/app/app.component.html' , 'structural-directives' )( format ="." )
39
+ .l-sub-section
40
+ :marked
41
+ There are two other kinds of Angular directives, described extensively elsewhere: (1) components and (2) attribute directives.
42
+
43
+ A *component* manages a region of HTML in the manner of a native HTML element.
44
+ Technically it's a directive with a template.
45
+
46
+ An [*attribute* directive](attribute-directives.html) changes the appearance or behavior of an element.
47
+ For example, the built-in [NgStyle](template-syntax.html#ngStyle) directive
48
+ changes several element styles at the same time.
49
+
44
50
45
51
<a id =" ngIf" ></a >
46
52
.l-main-section
@@ -261,176 +267,6 @@ figure.image-display
261
267
What does this have to do with `ngIf` and `ngFor`? We didn't use a `<template>` tag with those directives.
262
268
But Angular did.
263
269
264
- <a id =" ngIf" ></a >
265
- .l-main-section
266
- :marked
267
- ### NgIf
268
- We can add an element subtree (an element and its children) to the DOM by binding an `NgIf` directive to a #{_truthy} expression.
269
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgIf-1' )( format ="." )
270
-
271
- .alert.is-critical
272
- :marked
273
- Don't forget the asterisk (`*`) in front of `ngIf`.
274
- For more information, see [\* and <template>](#star-template).
275
- :marked
276
- Binding to a #{_falsy} expression removes the element subtree from the DOM.
277
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgIf-2' )( format ="." )
278
-
279
- block dart-no-truthy-falsy
280
- //- N/A
281
-
282
- :marked
283
- #### Visibility and NgIf are not the same
284
- We can show and hide an element subtree (the element and its children) with a
285
- [class](#class-binding) or [style](#style-binding) binding:
286
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgIf-3' )( format ="." )
287
- :marked
288
- Hiding a subtree is quite different from excluding a subtree with `NgIf`.
289
-
290
- When we hide the element subtree, it remains in the DOM.
291
- Components in the subtree are preserved, along with their state.
292
- Angular may continue to check for changes even to invisible properties.
293
- The subtree may tie up substantial memory and computing resources.
294
-
295
- When `NgIf` is `false`, Angular physically removes the element subtree from the DOM.
296
- It destroys components in the subtree, along with their state, potentially freeing up substantial resources and
297
- resulting in better performance for the user.
298
-
299
- The show/hide technique is probably fine for small element trees.
300
- We should be wary when hiding large trees; `NgIf` may be the safer choice. Always measure before leaping to conclusions.
301
-
302
- <a id =" ngSwitch" ></a >
303
- .l-main-section
304
- :marked
305
- ### NgSwitch
306
- We bind to `NgSwitch` when we want to display *one* element tree (an element and its children)
307
- from a *set* of possible element trees, based on some condition.
308
- Angular puts only the *selected* element tree into the DOM.
309
-
310
- Here's an example:
311
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgSwitch' )( format ="." )
312
- :marked
313
- We bind the parent `NgSwitch` directive to an expression returning a *switch value*.
314
- The value is a string in this example, but it can be a value of any type.
315
-
316
- In this example, the parent `NgSwitch` directive controls a set of child `<span>` elements.
317
- A `<span>` is either pegged to a *match value* expression or marked as the default.
318
-
319
- **At any particular moment, at most one of these *spans* is in the DOM.**
320
-
321
- If the *span*'s *match value* equals the switch value, Angular adds the `<span>` to the DOM.
322
- If none of the *spans* is a match, Angular adds the default *span* to the DOM.
323
- Angular removes and destroys all other *spans*.
324
- .l-sub-section
325
- :marked
326
- We could substitute any element for the *span* in this example.
327
- That element could be a `<div>` with a vast subtree of its own elements.
328
- Only the matching `<div>` and its subtree would appear in the DOM;
329
- the others would be removed.
330
- :marked
331
- Three collaborating directives are at work here:
332
- 1. `ngSwitch`: bound to an expression that returns the switch value
333
- 1. `ngSwitchCase`: bound to an expression returning a match value
334
- 1. `ngSwitchDefault`: a marker attribute on the default element
335
-
336
- .alert.is-critical
337
- :marked
338
- **Do *not*** put the asterisk (`*`) in front of `ngSwitch`. Use the property binding instead.
339
-
340
- **Do** put the asterisk (`*`) in front of `ngSwitchCase` and `ngSwitchDefault`.
341
- For more information, see [\* and <template>](#star-template).
342
-
343
- <a id =" ngFor" ></a >
344
- .l-main-section
345
- :marked
346
- ### NgFor
347
- `NgFor` is a _repeater_ directive — a way to customize data display.
348
-
349
- Our goal is to present a list of items. We define a block of HTML that defines how a single item should be displayed.
350
- We tell Angular to use that block as a template for rendering each item in the list.
351
-
352
- Here is an example of `NgFor` applied to a simple `<div>`:
353
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgFor-1' )( format ="." )
354
- :marked
355
- We can also apply an `NgFor` to a component element, as in this example:
356
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgFor-2' )( format ="." )
357
-
358
- .alert.is-critical
359
- :marked
360
- Don't forget the asterisk (`*`) in front of `ngFor`.
361
- For more information, see [\* and <template>](#star-template).
362
- :marked
363
- The text assigned to `*ngFor` is the instruction that guides the repeater process.
364
-
365
- <a id =" ngForMicrosyntax" ></a >
366
- :marked
367
- #### NgFor microsyntax
368
- The string assigned to `*ngFor` is not a [template expression](#template-expressions).
369
- It's a *microsyntax* — a little language of its own that Angular interprets. In this example, the string `"let hero of heroes"` means:
370
-
371
- > *Take each hero in the `heroes` #{_array}, store it in the local `hero` variable, and make it available to the templated HTML for each iteration.*
372
-
373
- Angular translates this instruction into a new set of elements and bindings.
374
-
375
- In the two previous examples, the `ngFor` directive iterates over the `heroes` #{_array} returned by the parent component's `heroes` property,
376
- stamping out instances of the element to which it is applied.
377
- Angular creates a fresh instance of the template for each hero in the array.
378
-
379
- The `let` keyword before `hero` creates a template input variable called `hero`.
380
-
381
- .alert.is-critical
382
- :marked
383
- A **template _input_ variable** is **_not_** the same as a [**template _reference_ variable**](#ref-vars),
384
- neither _semantically_ nor _syntactically_.
385
-
386
- The name in a template _input_ variable declaration is preceded by `let`.
387
- The name in a template _reference_ variable declaration is preceded by `#`.
388
-
389
- :marked
390
- We use this variable within the template to access a hero's properties,
391
- as we're doing in the interpolation.
392
- We can also pass the variable in a binding to a component element,
393
- as we're doing with `hero-detail`.
394
-
395
- :marked
396
- #### NgFor with index
397
- The `ngFor` directive supports an optional `index` that increases from 0 to the length of the array for each iteration.
398
- We can capture the index in a template input variable and use it in our template.
399
-
400
- The next example captures the index in a variable named `i`, using it to stamp out rows like "1 - Hercules Son of Zeus".
401
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgFor-3' )( format ="." )
402
- .l-sub-section
403
- :marked
404
- Learn about other special *index-like* values such as `last`, `even`, and `odd` in the [NgFor API reference](../api/common/index/NgFor-directive.html).
405
-
406
- :marked
407
- #### NgForTrackBy
408
- The `ngFor` directive has the potential to perform poorly, especially with large lists.
409
- A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations.
410
-
411
- For example, we could refresh the list of heroes by re-querying the server.
412
- The refreshed list probably contains most, if not all, of the previously displayed heroes.
413
-
414
- *We* know this because the `id` of each hero hasn't changed.
415
- But Angular sees only a fresh list of new object references.
416
- It has no choice but to tear down the old list, discard those DOM elements, and re-build a new list with new DOM elements.
417
-
418
- Angular can avoid this churn if we give it a *tracking* function that tells it what we know:
419
- that two objects with the same `hero.id` are the same *hero*. Here is such a function:
420
- + makeExample('template-syntax/ts/app/app.component.ts' , 'trackByHeroes' )( format ="." )
421
- :marked
422
- Now set the `NgForTrackBy` directive to that *tracking* function.
423
- + makeExample('template-syntax/ts/app/app.component.html' , 'NgForTrackBy-2' )( format ="." )
424
- :marked
425
- The *tracking* function doesn't eliminate all DOM changes.
426
- Angular may have to update the DOM element if the same-hero *properties* have changed.
427
- But if the properties haven't changed — and most of the time they will not have changed —
428
- Angular can leave those DOM elements alone. The list UI will be smoother and more responsive.
429
-
430
- Here is an illustration of the `NgForTrackBy` effect.
431
- figure.image-display
432
- img( src ='/resources/images/devguide/template-syntax/ng-for-track-by-anim.gif' alt ="NgForTrackBy" )
433
-
434
270
<a id =" asterisk" ></a >
435
271
.l-main-section
436
272
:marked
@@ -561,7 +397,7 @@ figure.image-display
561
397
.l-main-section
562
398
:marked
563
399
## Wrap up
564
- Here is the pertinent source for this chapter .
400
+ Here is the pertinent source for this guide .
565
401
566
402
+ makeTabs(`
567
403
structural-directives/ts/app/unless.directive.ts,
@@ -585,4 +421,4 @@ figure.image-display
585
421
and incorporate that content within their own templates.
586
422
Tab and tab pane controls are good examples.
587
423
588
- We'll learn about structural components in a future chapter .
424
+ We'll learn about structural components in a future guide .
0 commit comments