docs(lifecycle-hooks): post-RC5 Dart resync (#2141)
* lifecycle-hooks: copy latest to cache * docs(lifecycle-hooks): post-RC5 Dart resync - TS prose: removed mention of `routerOnActivate` which no longer exists in the new router. - TS improvements to the sample code have also been propagated to the Dart sample. Contributes to #2077. E2E tests pass now. * post-review updates
This commit is contained in:
parent
5d1da1e047
commit
f0daf5d238
@ -7,7 +7,7 @@ import 'logger_service.dart';
|
|||||||
//////////////////
|
//////////////////
|
||||||
// #docregion child-view
|
// #docregion child-view
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-child',
|
selector: 'my-child-view',
|
||||||
template: '<input [(ngModel)]="hero">')
|
template: '<input [(ngModel)]="hero">')
|
||||||
class ChildViewComponent {
|
class ChildViewComponent {
|
||||||
String hero = 'Magneta';
|
String hero = 'Magneta';
|
||||||
@ -20,7 +20,7 @@ class ChildViewComponent {
|
|||||||
// #docregion template
|
// #docregion template
|
||||||
template: '''
|
template: '''
|
||||||
<div>-- child view begins --</div>
|
<div>-- child view begins --</div>
|
||||||
<my-child></my-child>
|
<my-child-view></my-child-view>
|
||||||
<div>-- child view ends --</div>
|
<div>-- child view ends --</div>
|
||||||
<p *ngIf="comment.isNotEmpty" class="comment">{{comment}}</p>''',
|
<p *ngIf="comment.isNotEmpty" class="comment">{{comment}}</p>''',
|
||||||
// #enddocregion template
|
// #enddocregion template
|
||||||
|
@ -92,7 +92,7 @@ class DoCheckComponent implements DoCheck, OnChanges {
|
|||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'do-check-parent',
|
selector: 'do-check-parent',
|
||||||
templateUrl: 'on_changes_parent_component.html',
|
templateUrl: 'do_check_parent_component.html',
|
||||||
styles: const ['.parent {background: Lavender}'],
|
styles: const ['.parent {background: Lavender}'],
|
||||||
directives: const [DoCheckComponent])
|
directives: const [DoCheckComponent])
|
||||||
class DoCheckParentComponent {
|
class DoCheckParentComponent {
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
<div class="parent">
|
||||||
|
<h2>{{title}}</h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
|
||||||
|
<tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
|
||||||
|
</table>
|
||||||
|
<p><button (click)="reset()">Reset Log</button></p>
|
||||||
|
|
||||||
|
<!-- #docregion do-check -->
|
||||||
|
<do-check [hero]="hero" [power]="power"></do-check>
|
||||||
|
<!-- #enddocregion do-check -->
|
||||||
|
</div>
|
@ -7,8 +7,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<p><button (click)="reset()">Reset Log</button></p>
|
<p><button (click)="reset()">Reset Log</button></p>
|
||||||
|
|
||||||
<!-- #docregion on-changes -->
|
<!-- #docregion on-changes -->
|
||||||
<on-changes [hero]="hero" [power]="power"></on-changes>
|
<on-changes [hero]="hero" [power]="power"></on-changes>
|
||||||
<!-- #enddocregion on-changes -->
|
<!-- #enddocregion on-changes -->
|
||||||
<do-check [hero]="hero" [power]="power"></do-check>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// #docregion
|
|
||||||
import 'package:angular2/platform/browser.dart';
|
import 'package:angular2/platform/browser.dart';
|
||||||
|
|
||||||
import 'package:lifecycle_hooks/app_component.dart';
|
import 'package:lifecycle_hooks/app_component.dart';
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
bootstrap(AppComponent);
|
bootstrap(AppComponent);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// #docregion
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<p><button (click)="reset()">Reset Log</button></p>
|
<p><button (click)="reset()">Reset Log</button></p>
|
||||||
|
|
||||||
<!-- #docregion on-changes -->
|
<!-- #docregion on-changes -->
|
||||||
<on-changes [hero]="hero" [power]="power"></on-changes>
|
<on-changes [hero]="hero" [power]="power"></on-changes>
|
||||||
<!-- #enddocregion on-changes -->
|
<!-- #enddocregion on-changes -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,9 +3,16 @@ extends ../../../ts/_cache/guide/lifecycle-hooks.jade
|
|||||||
block includes
|
block includes
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
block optional-interfaces
|
block other-angular-subsystems
|
||||||
//- n/a for Dart
|
:marked
|
||||||
|
The router, for instance, also has its own [router lifecycle
|
||||||
|
hooks](router.html#router-lifecycle-hooks) that allow us to tap into
|
||||||
|
specific moments in route navigation.
|
||||||
|
A parallel can be drawn between `ngOnInit` and `routerOnActivate`. Both are
|
||||||
|
prefixed so as to avoid collision, and both run right when a component is
|
||||||
|
being initialized.
|
||||||
|
|
||||||
block tick-methods
|
block tick-methods
|
||||||
:marked
|
:marked
|
||||||
The `LoggerService.tick` method, which returns a `Future`, postpones the update one turn of the of the browser's update cycle ... and that's long enough.
|
The `LoggerService.tick` method, which returns a `Future`, postpones the
|
||||||
|
update one turn of the of the browser's update cycle ... and that's long enough.
|
||||||
|
@ -12,13 +12,13 @@ block includes
|
|||||||
that give us visibility into these key moments and the ability to act when they occur.
|
that give us visibility into these key moments and the ability to act when they occur.
|
||||||
|
|
||||||
We cover these hooks in this chapter and demonstrate how they work in code.
|
We cover these hooks in this chapter and demonstrate how they work in code.
|
||||||
|
|
||||||
* [The lifecycle hooks](#hooks-overview)
|
* [The lifecycle hooks](#hooks-overview)
|
||||||
* [The hook-call sequence](#hook-sequence)
|
* [The hook-call sequence](#hook-sequence)
|
||||||
* [Other Angular lifecycle hooks](#other-lifecycles)
|
* [Other Angular lifecycle hooks](#other-lifecycles)
|
||||||
* [The lifecycle sample](#the-sample)
|
* [The lifecycle sample](#the-sample)
|
||||||
* [All](#peek-a-boo)
|
* [All](#peek-a-boo)
|
||||||
* [Spying OnInit and OnDestroy](#spy)
|
* [Spying OnInit and OnDestroy](#spy)
|
||||||
* [OnChanges](#onchanges)
|
* [OnChanges](#onchanges)
|
||||||
* [DoCheck](#docheck)
|
* [DoCheck](#docheck)
|
||||||
* [AfterViewInit and AfterViewChecked](#afterview)
|
* [AfterViewInit and AfterViewChecked](#afterview)
|
||||||
@ -35,7 +35,7 @@ a#hooks-overview
|
|||||||
|
|
||||||
Developers can tap into key moments in that lifecycle by implementing
|
Developers can tap into key moments in that lifecycle by implementing
|
||||||
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
|
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
|
||||||
|
|
||||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
||||||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||||
We might implement it in a component class like this:
|
We might implement it in a component class like this:
|
||||||
@ -43,12 +43,14 @@ a#hooks-overview
|
|||||||
:marked
|
:marked
|
||||||
No directive or component will implement all of them and some of the hooks only make sense for components.
|
No directive or component will implement all of them and some of the hooks only make sense for components.
|
||||||
Angular only calls a directive/component hook method *if it is defined*.
|
Angular only calls a directive/component hook method *if it is defined*.
|
||||||
block optional-interfaces
|
|
||||||
|
+ifDocsFor('ts|js')
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
### Interface optional?
|
### Interface optional?
|
||||||
|
|
||||||
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
||||||
The JavaScript language doesn't have interfaces.
|
The JavaScript language doesn't have interfaces.
|
||||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
||||||
|
|
||||||
Fortunately, they aren't necessary.
|
Fortunately, they aren't necessary.
|
||||||
@ -58,8 +60,8 @@ block optional-interfaces
|
|||||||
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
||||||
|
|
||||||
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
||||||
in order to benefit from strong typing and editor tooling.
|
in order to benefit from strong typing and editor tooling.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here are the component lifecycle hook methods:
|
Here are the component lifecycle hook methods:
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ block optional-interfaces
|
|||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Purpose
|
th Purpose
|
||||||
@ -80,19 +82,19 @@ table(width="100%")
|
|||||||
td ngOnChanges
|
td ngOnChanges
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Respond after Angular sets a data-bound input property.
|
Respond after Angular sets a data-bound input property.
|
||||||
The method receives a `changes` object of current and previous values.
|
The method receives a `changes` object of current and previous values.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td ngDoCheck
|
td ngDoCheck
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Detect and act upon changes that Angular can or won't
|
Detect and act upon changes that Angular can't or won't
|
||||||
detect on its own. Called every change detection run.
|
detect on its own. Called every change detection run.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td ngOnDestroy
|
td ngOnDestroy
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Cleanup just before Angular destroys the directive/component.
|
Cleanup just before Angular destroys the directive/component.
|
||||||
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
@ -100,7 +102,7 @@ table(width="100%")
|
|||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Purpose
|
th Purpose
|
||||||
@ -131,11 +133,11 @@ a(id="hook-sequence")
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Lifecycle sequence
|
## Lifecycle sequence
|
||||||
*After* Angular creates a component/directive by `new`-ing its constructor,
|
*After* Angular creates a component/directive by `new`-ing its constructor,
|
||||||
it calls the lifecycle hook methods in the following sequence at specific moments:
|
it calls the lifecycle hook methods in the following sequence at specific moments:
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Timing
|
th Timing
|
||||||
@ -184,14 +186,13 @@ a(id="other-lifecycles")
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Other lifecycle hooks
|
## Other lifecycle hooks
|
||||||
|
|
||||||
Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed.
|
Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed.
|
||||||
The router, for instance, also has it's own [router lifecycle hooks](router.html#router-lifecycle-hooks)
|
|
||||||
that allow us to tap into specific moments in route navigation.
|
block other-angular-subsystems
|
||||||
|
//- N/A for TS.
|
||||||
A parallel can be drawn between `ngOnInit` and `routerOnActivate`.
|
|
||||||
Both are prefixed so as to avoid collision, and both run right when a component is 'booting' up.
|
:marked
|
||||||
|
|
||||||
3rd party libraries might implement their hooks as well in order to give us, the developers, more
|
3rd party libraries might implement their hooks as well in order to give us, the developers, more
|
||||||
control over how these libraries are used.
|
control over how these libraries are used.
|
||||||
|
|
||||||
@ -203,14 +204,14 @@ a(id="other-lifecycles")
|
|||||||
demonstrates the lifecycle hooks in action through a series of exercises
|
demonstrates the lifecycle hooks in action through a series of exercises
|
||||||
presented as components under the control of the root `AppComponent`.
|
presented as components under the control of the root `AppComponent`.
|
||||||
|
|
||||||
They follow a common pattern: a *parent* component serves as a test rig for
|
They follow a common pattern: a *parent* component serves as a test rig for
|
||||||
a *child* component that illustrates one or more of the lifecycle hook methods.
|
a *child* component that illustrates one or more of the lifecycle hook methods.
|
||||||
|
|
||||||
Here's a brief description of each exercise:
|
Here's a brief description of each exercise:
|
||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Component
|
th Component
|
||||||
th Description
|
th Description
|
||||||
@ -225,9 +226,9 @@ table(width="100%")
|
|||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Directives have lifecycle hooks too.
|
Directives have lifecycle hooks too.
|
||||||
We create a `SpyDirective` that logs when the element it spies upon is
|
We create a `SpyDirective` that logs when the element it spies upon is
|
||||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
||||||
|
|
||||||
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||||
managed by the parent `SpyComponent`.
|
managed by the parent `SpyComponent`.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
@ -238,7 +239,7 @@ table(width="100%")
|
|||||||
every time one of the component input properties changes.
|
every time one of the component input properties changes.
|
||||||
Shows how to interpret the `changes` object.
|
Shows how to interpret the `changes` object.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td <a href="#docheck">DoCheck</a>
|
td <a href="#docheck">DoCheck</a>
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Implements an `ngDoCheck` method with custom change detection.
|
Implements an `ngDoCheck` method with custom change detection.
|
||||||
@ -260,14 +261,14 @@ table(width="100%")
|
|||||||
td Counter
|
td Counter
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Demonstrates a combination of a component and a directive
|
Demonstrates a combination of a component and a directive
|
||||||
each with its own hooks.
|
each with its own hooks.
|
||||||
|
|
||||||
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
||||||
every time the parent component increments its input counter property.
|
every time the parent component increments its input counter property.
|
||||||
Meanwhile, we apply the `SpyDirective` from the previous example
|
Meanwhile, we apply the `SpyDirective` from the previous example
|
||||||
to the `CounterComponent` log and watch log entries be created and destroyed.
|
to the `CounterComponent` log and watch log entries be created and destroyed.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
||||||
|
|
||||||
@ -279,23 +280,23 @@ a(id="peek-a-boo")
|
|||||||
|
|
||||||
In real life, we'd rarely if ever implement all of the interfaces like this.
|
In real life, we'd rarely if ever implement all of the interfaces like this.
|
||||||
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
|
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
|
||||||
|
|
||||||
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
||||||
:marked
|
:marked
|
||||||
The sequence of log messages follows the prescribed hook calling order:
|
The sequence of log messages follows the prescribed hook calling order:
|
||||||
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
||||||
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The constructor isn't an Angular hook *per se*.
|
The constructor isn't an Angular hook *per se*.
|
||||||
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
|
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
|
||||||
:marked
|
:marked
|
||||||
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
||||||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||||
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
||||||
as lean as possible!
|
as lean as possible!
|
||||||
|
|
||||||
Our next examples focus on hook details.
|
Our next examples focus on hook details.
|
||||||
@ -304,74 +305,74 @@ figure.image-display
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Spying *OnInit* and *OnDestroy*
|
## Spying *OnInit* and *OnDestroy*
|
||||||
|
|
||||||
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
|
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
|
||||||
but we don't want *it* to know we're watching.
|
but we don't want *it* to know we're watching.
|
||||||
|
|
||||||
This is the perfect infiltration job for a directive.
|
This is the perfect infiltration job for a directive.
|
||||||
Our heroes will never know it's there.
|
Our heroes will never know it's there.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Kidding aside, we're emphasizing two key points:
|
Kidding aside, we're emphasizing two key points:
|
||||||
|
|
||||||
1. Angular calls hook methods for *directives* as well as components.
|
1. Angular calls hook methods for *directives* as well as components.
|
||||||
|
|
||||||
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
|
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
|
||||||
Obviously we can't change the implementation of a native `div`.
|
Obviously we can't change the implementation of a native `div`.
|
||||||
We can't modify a third party component either.
|
We can't modify a third party component either.
|
||||||
But we can watch both with a directive.
|
But we can watch both with a directive.
|
||||||
|
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
||||||
that log messages to the parent via an injected `LoggerService`.
|
that log messages to the parent via an injected `LoggerService`.
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||||
at the same time as that element.
|
at the same time as that element.
|
||||||
Here we attach it to the repeated hero `<div>`
|
Here we attach it to the repeated hero `<div>`
|
||||||
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
||||||
with an entry in the *Hook Log* as we see here:
|
with an entry in the *Hook Log* as we see here:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
||||||
We see a new entry for each hero.
|
We see a new entry for each hero.
|
||||||
|
|
||||||
The *Reset* button clears the `heroes` list.
|
The *Reset* button clears the `heroes` list.
|
||||||
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
|
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
|
||||||
The spy's `ngOnDestroy` method reports its last moments.
|
The spy's `ngOnDestroy` method reports its last moments.
|
||||||
|
|
||||||
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
||||||
Let's see why we need them.
|
Let's see why we need them.
|
||||||
|
|
||||||
### OnInit
|
### OnInit
|
||||||
|
|
||||||
We turn to `ngOnInit` for two main reasons:
|
We turn to `ngOnInit` for two main reasons:
|
||||||
1. To perform complex initializations shortly after construction
|
1. To perform complex initializations shortly after construction
|
||||||
1. To set up the component after Angular sets the input properties
|
1. To set up the component after Angular sets the input properties
|
||||||
|
|
||||||
An `ngOnInit` often fetches data for the component as shown in the
|
An `ngOnInit` often fetches data for the component as shown in the
|
||||||
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
|
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
|
||||||
|
|
||||||
We don't fetch data in a component constructor. Why?
|
We don't fetch data in a component constructor. Why?
|
||||||
Because experienced developers agree that components should be cheap and safe to construct.
|
Because experienced developers agree that components should be cheap and safe to construct.
|
||||||
We shouldn't worry that a new component will try to contact a remote server when
|
We shouldn't worry that a new component will try to contact a remote server when
|
||||||
created under test or before we decide to display it.
|
created under test or before we decide to display it.
|
||||||
Constructors should do no more than set the initial local variables to simple values.
|
Constructors should do no more than set the initial local variables to simple values.
|
||||||
|
|
||||||
When a component must start working _soon_ after creation,
|
When a component must start working _soon_ after creation,
|
||||||
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
||||||
That's where the heavy initialization logic belongs.
|
That's where the heavy initialization logic belongs.
|
||||||
|
|
||||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
||||||
That's a problem if we need to initialize the directive based on those properties.
|
That's a problem if we need to initialize the directive based on those properties.
|
||||||
They'll have been set when our `ngOninit` runs.
|
They'll have been set when our `ngOninit` runs.
|
||||||
@ -382,11 +383,11 @@ figure.image-display
|
|||||||
It only calls `ngOnit` once.
|
It only calls `ngOnit` once.
|
||||||
:marked
|
:marked
|
||||||
### OnDestroy
|
### OnDestroy
|
||||||
|
|
||||||
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
|
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
|
||||||
|
|
||||||
This is the time to notify another part of the application that this component is going away.
|
This is the time to notify another part of the application that this component is going away.
|
||||||
|
|
||||||
This is the place to free resources that won't be garbage collected automatically.
|
This is the place to free resources that won't be garbage collected automatically.
|
||||||
Unsubscribe from observables and DOM events. Stop interval timers.
|
Unsubscribe from observables and DOM events. Stop interval timers.
|
||||||
Unregister all callbacks that this directive registered with global or application services.
|
Unregister all callbacks that this directive registered with global or application services.
|
||||||
@ -395,17 +396,17 @@ figure.image-display
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## OnChanges
|
## OnChanges
|
||||||
|
|
||||||
We monitor the `OnChanges` hook in this example.
|
We monitor the `OnChanges` hook in this example.
|
||||||
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
|
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
|
||||||
|
|
||||||
Here is our implementation of the hook.
|
Here is our implementation of the hook.
|
||||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `ngOnChanges` method takes an object that maps each changed property name to a
|
The `ngOnChanges` method takes an object that maps each changed property name to a
|
||||||
[SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values.
|
[SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values.
|
||||||
We iterate over the changed properties and log them.
|
We iterate over the changed properties and log them.
|
||||||
|
|
||||||
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
||||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
||||||
:marked
|
:marked
|
||||||
@ -417,14 +418,14 @@ figure.image-display
|
|||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
||||||
That's surprising at first.
|
That's surprising at first.
|
||||||
|
|
||||||
Angular only calls the hook when the value of the input property changes.
|
Angular only calls the hook when the value of the input property changes.
|
||||||
The value of the `hero` property is the *reference to the hero object*.
|
The value of the `hero` property is the *reference to the hero object*.
|
||||||
Angular doesn't care that the hero's own `name` property changed.
|
Angular doesn't care that the hero's own `name` property changed.
|
||||||
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
@ -433,27 +434,27 @@ figure.image-display
|
|||||||
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
With this method we can detect a change that Angular overlooked.
|
With this method we can detect a change that Angular overlooked.
|
||||||
What we do with that information to refresh the display is a separate matter.
|
What we do with that information to refresh the display is a separate matter.
|
||||||
:marked
|
:marked
|
||||||
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
||||||
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We manually check everything that we care about, capturing and comparing against previous values.
|
We manually check everything that we care about, capturing and comparing against previous values.
|
||||||
We write a special message to the log when there are no substantive changes
|
We write a special message to the log when there are no substantive changes
|
||||||
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
||||||
|
|
||||||
The results are illuminating:
|
The results are illuminating:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
||||||
:marked
|
:marked
|
||||||
We now are able to detect when the hero's `name` has changed. But we must be careful.
|
We now are able to detect when the hero's `name` has changed. But we must be careful.
|
||||||
|
|
||||||
The `ngDoCheck` hook is called with enormous frequency —
|
The `ngDoCheck` hook is called with enormous frequency —
|
||||||
after _every_ change detection cycle no matter where the change occurred.
|
after _every_ change detection cycle no matter where the change occurred.
|
||||||
It's called over twenty times in this example before the user can do anything.
|
It's called over twenty times in this example before the user can do anything.
|
||||||
|
|
||||||
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
||||||
Mere mousing into another input box triggers a call.
|
Mere mousing into another input box triggers a call.
|
||||||
Relatively few calls reveal actual changes to pertinent data.
|
Relatively few calls reveal actual changes to pertinent data.
|
||||||
@ -461,7 +462,7 @@ figure.image-display
|
|||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We also see that the `ngOnChanges` method is called in contradiction of the
|
We also see that the `ngOnChanges` method is called in contradiction of the
|
||||||
[incorrect API documentation](../api/core/index/DoCheck-class.html).
|
[incorrect API documentation](../api/core/index/DoCheck-class.html).
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
@ -469,15 +470,15 @@ figure.image-display
|
|||||||
## AfterView
|
## AfterView
|
||||||
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
|
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
|
||||||
*after* it creates a component's child views.
|
*after* it creates a component's child views.
|
||||||
|
|
||||||
Here's a child view that displays a hero's name in an input box:
|
Here's a child view that displays a hero's name in an input box:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `AfterViewComponent` displays this child view *within its template*:
|
The `AfterViewComponent` displays this child view *within its template*:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The following hooks take action based on changing values *within the child view*
|
The following hooks take action based on changing values *within the child view*
|
||||||
which we can only reach by querying for the child view via the property decorated with
|
which we can only reach by querying for the child view via the property decorated with
|
||||||
[@ViewChild](../api/core/index/ViewChild-var.html).
|
[@ViewChild](../api/core/index/ViewChild-var.html).
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
||||||
@ -485,15 +486,15 @@ figure.image-display
|
|||||||
:marked
|
:marked
|
||||||
### Abide by the unidirectional data flow rule
|
### Abide by the unidirectional data flow rule
|
||||||
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
Why does the `doSomething` method wait a tick before updating `comment`?
|
Why does the `doSomething` method wait a tick before updating `comment`?
|
||||||
|
|
||||||
Because we must adhere to Angular's unidirectional data flow rule which says that
|
Because we must adhere to Angular's unidirectional data flow rule which says that
|
||||||
we may not update the view *after* it has been composed.
|
we may not update the view *after* it has been composed.
|
||||||
Both hooks fire after the component's view has been composed.
|
Both hooks fire after the component's view has been composed.
|
||||||
|
|
||||||
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
|
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
|
||||||
block tick-methods
|
block tick-methods
|
||||||
:marked
|
:marked
|
||||||
@ -512,19 +513,19 @@ figure.image-display
|
|||||||
## AfterContent
|
## AfterContent
|
||||||
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
|
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
|
||||||
*after* Angular projects external content into the component.
|
*after* Angular projects external content into the component.
|
||||||
|
|
||||||
### Content projection
|
### Content projection
|
||||||
*Content projection* is a way to import HTML content from outside the component and insert that content
|
*Content projection* is a way to import HTML content from outside the component and insert that content
|
||||||
into the component's template in a designated spot.
|
into the component's template in a designated spot.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Angular 1 developers know this technique as *transclusion*.
|
Angular 1 developers know this technique as *transclusion*.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We'll illustrate with a variation on the [previous](#afterview) example
|
We'll illustrate with a variation on the [previous](#afterview) example
|
||||||
whose behavior and output is almost the same.
|
whose behavior and output is almost the same.
|
||||||
|
|
||||||
This time, instead of including the child view within the template, we'll import it from
|
This time, instead of including the child view within the template, we'll import it from
|
||||||
the `AfterContentComponent`'s parent. Here's the parent's template.
|
the `AfterContentComponent`'s parent. Here's the parent's template.
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
|
||||||
@ -532,11 +533,11 @@ figure.image-display
|
|||||||
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
|
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
|
||||||
We never put content between a component's element tags *unless we intend to project that content
|
We never put content between a component's element tags *unless we intend to project that content
|
||||||
into the component*.
|
into the component*.
|
||||||
|
|
||||||
Now look at the component's template:
|
Now look at the component's template:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `<ng-content>` tag is a *placeholder* for the external content.
|
The `<ng-content>` tag is a *placeholder* for the external content.
|
||||||
They tell Angular where to insert that content.
|
They tell Angular where to insert that content.
|
||||||
In this case, the projected content is the `<my-child>` from the parent.
|
In this case, the projected content is the `<my-child>` from the parent.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
@ -549,26 +550,26 @@ figure.image-display
|
|||||||
:marked
|
:marked
|
||||||
### AfterContent hooks
|
### AfterContent hooks
|
||||||
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
||||||
that we're looking for.
|
that we're looking for.
|
||||||
|
|
||||||
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
||||||
appear *within* the component's template.
|
appear *within* the component's template.
|
||||||
|
|
||||||
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
|
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
|
||||||
projected into the component.
|
projected into the component.
|
||||||
|
|
||||||
The following *AfterContent* hooks take action based on changing values in a *content child*
|
The following *AfterContent* hooks take action based on changing values in a *content child*
|
||||||
which we can only reach by querying for it via the property decorated with
|
which we can only reach by querying for it via the property decorated with
|
||||||
[@ContentChild](../api/core/index/ContentChild-var.html).
|
[@ContentChild](../api/core/index/ContentChild-var.html).
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
### No unidirectional flow worries
|
### No unidirectional flow worries
|
||||||
|
|
||||||
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
||||||
There's no [need to wait](#wait-a-tick).
|
There's no [need to wait](#wait-a-tick).
|
||||||
|
|
||||||
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
||||||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||||
We still have a window of opportunity to modify that view.
|
We still have a window of opportunity to modify that view.
|
||||||
|
@ -12,13 +12,13 @@ block includes
|
|||||||
that give us visibility into these key moments and the ability to act when they occur.
|
that give us visibility into these key moments and the ability to act when they occur.
|
||||||
|
|
||||||
We cover these hooks in this chapter and demonstrate how they work in code.
|
We cover these hooks in this chapter and demonstrate how they work in code.
|
||||||
|
|
||||||
* [The lifecycle hooks](#hooks-overview)
|
* [The lifecycle hooks](#hooks-overview)
|
||||||
* [The hook-call sequence](#hook-sequence)
|
* [The hook-call sequence](#hook-sequence)
|
||||||
* [Other Angular lifecycle hooks](#other-lifecycles)
|
* [Other Angular lifecycle hooks](#other-lifecycles)
|
||||||
* [The lifecycle sample](#the-sample)
|
* [The lifecycle sample](#the-sample)
|
||||||
* [All](#peek-a-boo)
|
* [All](#peek-a-boo)
|
||||||
* [Spying OnInit and OnDestroy](#spy)
|
* [Spying OnInit and OnDestroy](#spy)
|
||||||
* [OnChanges](#onchanges)
|
* [OnChanges](#onchanges)
|
||||||
* [DoCheck](#docheck)
|
* [DoCheck](#docheck)
|
||||||
* [AfterViewInit and AfterViewChecked](#afterview)
|
* [AfterViewInit and AfterViewChecked](#afterview)
|
||||||
@ -35,7 +35,7 @@ a#hooks-overview
|
|||||||
|
|
||||||
Developers can tap into key moments in that lifecycle by implementing
|
Developers can tap into key moments in that lifecycle by implementing
|
||||||
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
|
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
|
||||||
|
|
||||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
||||||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||||
We might implement it in a component class like this:
|
We might implement it in a component class like this:
|
||||||
@ -43,12 +43,14 @@ a#hooks-overview
|
|||||||
:marked
|
:marked
|
||||||
No directive or component will implement all of them and some of the hooks only make sense for components.
|
No directive or component will implement all of them and some of the hooks only make sense for components.
|
||||||
Angular only calls a directive/component hook method *if it is defined*.
|
Angular only calls a directive/component hook method *if it is defined*.
|
||||||
block optional-interfaces
|
|
||||||
|
+ifDocsFor('ts|js')
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
### Interface optional?
|
### Interface optional?
|
||||||
|
|
||||||
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
||||||
The JavaScript language doesn't have interfaces.
|
The JavaScript language doesn't have interfaces.
|
||||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
||||||
|
|
||||||
Fortunately, they aren't necessary.
|
Fortunately, they aren't necessary.
|
||||||
@ -58,8 +60,8 @@ block optional-interfaces
|
|||||||
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
||||||
|
|
||||||
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
||||||
in order to benefit from strong typing and editor tooling.
|
in order to benefit from strong typing and editor tooling.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here are the component lifecycle hook methods:
|
Here are the component lifecycle hook methods:
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ block optional-interfaces
|
|||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Purpose
|
th Purpose
|
||||||
@ -80,7 +82,7 @@ table(width="100%")
|
|||||||
td ngOnChanges
|
td ngOnChanges
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Respond after Angular sets a data-bound input property.
|
Respond after Angular sets a data-bound input property.
|
||||||
The method receives a `changes` object of current and previous values.
|
The method receives a `changes` object of current and previous values.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td ngDoCheck
|
td ngDoCheck
|
||||||
@ -92,7 +94,7 @@ table(width="100%")
|
|||||||
td ngOnDestroy
|
td ngOnDestroy
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Cleanup just before Angular destroys the directive/component.
|
Cleanup just before Angular destroys the directive/component.
|
||||||
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
@ -100,7 +102,7 @@ table(width="100%")
|
|||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Purpose
|
th Purpose
|
||||||
@ -131,11 +133,11 @@ a(id="hook-sequence")
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Lifecycle sequence
|
## Lifecycle sequence
|
||||||
*After* Angular creates a component/directive by `new`-ing its constructor,
|
*After* Angular creates a component/directive by `new`-ing its constructor,
|
||||||
it calls the lifecycle hook methods in the following sequence at specific moments:
|
it calls the lifecycle hook methods in the following sequence at specific moments:
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Hook
|
th Hook
|
||||||
th Timing
|
th Timing
|
||||||
@ -184,12 +186,13 @@ a(id="other-lifecycles")
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Other lifecycle hooks
|
## Other lifecycle hooks
|
||||||
|
|
||||||
Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed.
|
Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed.
|
||||||
|
|
||||||
A parallel can be drawn between `ngOnInit` and `routerOnActivate`.
|
block other-angular-subsystems
|
||||||
Both are prefixed so as to avoid collision, and both run right when a component is 'booting' up.
|
//- N/A for TS.
|
||||||
|
|
||||||
|
:marked
|
||||||
3rd party libraries might implement their hooks as well in order to give us, the developers, more
|
3rd party libraries might implement their hooks as well in order to give us, the developers, more
|
||||||
control over how these libraries are used.
|
control over how these libraries are used.
|
||||||
|
|
||||||
@ -201,14 +204,14 @@ a(id="other-lifecycles")
|
|||||||
demonstrates the lifecycle hooks in action through a series of exercises
|
demonstrates the lifecycle hooks in action through a series of exercises
|
||||||
presented as components under the control of the root `AppComponent`.
|
presented as components under the control of the root `AppComponent`.
|
||||||
|
|
||||||
They follow a common pattern: a *parent* component serves as a test rig for
|
They follow a common pattern: a *parent* component serves as a test rig for
|
||||||
a *child* component that illustrates one or more of the lifecycle hook methods.
|
a *child* component that illustrates one or more of the lifecycle hook methods.
|
||||||
|
|
||||||
Here's a brief description of each exercise:
|
Here's a brief description of each exercise:
|
||||||
|
|
||||||
table(width="100%")
|
table(width="100%")
|
||||||
col(width="20%")
|
col(width="20%")
|
||||||
col(width="80%")
|
col(width="80%")
|
||||||
tr
|
tr
|
||||||
th Component
|
th Component
|
||||||
th Description
|
th Description
|
||||||
@ -223,9 +226,9 @@ table(width="100%")
|
|||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Directives have lifecycle hooks too.
|
Directives have lifecycle hooks too.
|
||||||
We create a `SpyDirective` that logs when the element it spies upon is
|
We create a `SpyDirective` that logs when the element it spies upon is
|
||||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
||||||
|
|
||||||
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||||
managed by the parent `SpyComponent`.
|
managed by the parent `SpyComponent`.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
@ -236,7 +239,7 @@ table(width="100%")
|
|||||||
every time one of the component input properties changes.
|
every time one of the component input properties changes.
|
||||||
Shows how to interpret the `changes` object.
|
Shows how to interpret the `changes` object.
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td <a href="#docheck">DoCheck</a>
|
td <a href="#docheck">DoCheck</a>
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Implements an `ngDoCheck` method with custom change detection.
|
Implements an `ngDoCheck` method with custom change detection.
|
||||||
@ -258,14 +261,14 @@ table(width="100%")
|
|||||||
td Counter
|
td Counter
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
Demonstrates a combination of a component and a directive
|
Demonstrates a combination of a component and a directive
|
||||||
each with its own hooks.
|
each with its own hooks.
|
||||||
|
|
||||||
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
||||||
every time the parent component increments its input counter property.
|
every time the parent component increments its input counter property.
|
||||||
Meanwhile, we apply the `SpyDirective` from the previous example
|
Meanwhile, we apply the `SpyDirective` from the previous example
|
||||||
to the `CounterComponent` log and watch log entries be created and destroyed.
|
to the `CounterComponent` log and watch log entries be created and destroyed.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
||||||
|
|
||||||
@ -277,23 +280,23 @@ a(id="peek-a-boo")
|
|||||||
|
|
||||||
In real life, we'd rarely if ever implement all of the interfaces like this.
|
In real life, we'd rarely if ever implement all of the interfaces like this.
|
||||||
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
|
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
|
||||||
|
|
||||||
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
||||||
:marked
|
:marked
|
||||||
The sequence of log messages follows the prescribed hook calling order:
|
The sequence of log messages follows the prescribed hook calling order:
|
||||||
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
||||||
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The constructor isn't an Angular hook *per se*.
|
The constructor isn't an Angular hook *per se*.
|
||||||
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
|
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
|
||||||
:marked
|
:marked
|
||||||
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
||||||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||||
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
||||||
as lean as possible!
|
as lean as possible!
|
||||||
|
|
||||||
Our next examples focus on hook details.
|
Our next examples focus on hook details.
|
||||||
@ -302,74 +305,74 @@ figure.image-display
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Spying *OnInit* and *OnDestroy*
|
## Spying *OnInit* and *OnDestroy*
|
||||||
|
|
||||||
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
|
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
|
||||||
but we don't want *it* to know we're watching.
|
but we don't want *it* to know we're watching.
|
||||||
|
|
||||||
This is the perfect infiltration job for a directive.
|
This is the perfect infiltration job for a directive.
|
||||||
Our heroes will never know it's there.
|
Our heroes will never know it's there.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Kidding aside, we're emphasizing two key points:
|
Kidding aside, we're emphasizing two key points:
|
||||||
|
|
||||||
1. Angular calls hook methods for *directives* as well as components.
|
1. Angular calls hook methods for *directives* as well as components.
|
||||||
|
|
||||||
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
|
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
|
||||||
Obviously we can't change the implementation of a native `div`.
|
Obviously we can't change the implementation of a native `div`.
|
||||||
We can't modify a third party component either.
|
We can't modify a third party component either.
|
||||||
But we can watch both with a directive.
|
But we can watch both with a directive.
|
||||||
|
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
||||||
that log messages to the parent via an injected `LoggerService`.
|
that log messages to the parent via an injected `LoggerService`.
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||||
at the same time as that element.
|
at the same time as that element.
|
||||||
Here we attach it to the repeated hero `<div>`
|
Here we attach it to the repeated hero `<div>`
|
||||||
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
||||||
with an entry in the *Hook Log* as we see here:
|
with an entry in the *Hook Log* as we see here:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
||||||
We see a new entry for each hero.
|
We see a new entry for each hero.
|
||||||
|
|
||||||
The *Reset* button clears the `heroes` list.
|
The *Reset* button clears the `heroes` list.
|
||||||
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
|
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
|
||||||
The spy's `ngOnDestroy` method reports its last moments.
|
The spy's `ngOnDestroy` method reports its last moments.
|
||||||
|
|
||||||
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
||||||
Let's see why we need them.
|
Let's see why we need them.
|
||||||
|
|
||||||
### OnInit
|
### OnInit
|
||||||
|
|
||||||
We turn to `ngOnInit` for two main reasons:
|
We turn to `ngOnInit` for two main reasons:
|
||||||
1. To perform complex initializations shortly after construction
|
1. To perform complex initializations shortly after construction
|
||||||
1. To set up the component after Angular sets the input properties
|
1. To set up the component after Angular sets the input properties
|
||||||
|
|
||||||
An `ngOnInit` often fetches data for the component as shown in the
|
An `ngOnInit` often fetches data for the component as shown in the
|
||||||
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
|
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
|
||||||
|
|
||||||
We don't fetch data in a component constructor. Why?
|
We don't fetch data in a component constructor. Why?
|
||||||
Because experienced developers agree that components should be cheap and safe to construct.
|
Because experienced developers agree that components should be cheap and safe to construct.
|
||||||
We shouldn't worry that a new component will try to contact a remote server when
|
We shouldn't worry that a new component will try to contact a remote server when
|
||||||
created under test or before we decide to display it.
|
created under test or before we decide to display it.
|
||||||
Constructors should do no more than set the initial local variables to simple values.
|
Constructors should do no more than set the initial local variables to simple values.
|
||||||
|
|
||||||
When a component must start working _soon_ after creation,
|
When a component must start working _soon_ after creation,
|
||||||
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
||||||
That's where the heavy initialization logic belongs.
|
That's where the heavy initialization logic belongs.
|
||||||
|
|
||||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
||||||
That's a problem if we need to initialize the directive based on those properties.
|
That's a problem if we need to initialize the directive based on those properties.
|
||||||
They'll have been set when our `ngOninit` runs.
|
They'll have been set when our `ngOninit` runs.
|
||||||
@ -380,11 +383,11 @@ figure.image-display
|
|||||||
It only calls `ngOnit` once.
|
It only calls `ngOnit` once.
|
||||||
:marked
|
:marked
|
||||||
### OnDestroy
|
### OnDestroy
|
||||||
|
|
||||||
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
|
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
|
||||||
|
|
||||||
This is the time to notify another part of the application that this component is going away.
|
This is the time to notify another part of the application that this component is going away.
|
||||||
|
|
||||||
This is the place to free resources that won't be garbage collected automatically.
|
This is the place to free resources that won't be garbage collected automatically.
|
||||||
Unsubscribe from observables and DOM events. Stop interval timers.
|
Unsubscribe from observables and DOM events. Stop interval timers.
|
||||||
Unregister all callbacks that this directive registered with global or application services.
|
Unregister all callbacks that this directive registered with global or application services.
|
||||||
@ -393,17 +396,17 @@ figure.image-display
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## OnChanges
|
## OnChanges
|
||||||
|
|
||||||
We monitor the `OnChanges` hook in this example.
|
We monitor the `OnChanges` hook in this example.
|
||||||
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
|
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
|
||||||
|
|
||||||
Here is our implementation of the hook.
|
Here is our implementation of the hook.
|
||||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `ngOnChanges` method takes an object that maps each changed property name to a
|
The `ngOnChanges` method takes an object that maps each changed property name to a
|
||||||
[SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values.
|
[SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values.
|
||||||
We iterate over the changed properties and log them.
|
We iterate over the changed properties and log them.
|
||||||
|
|
||||||
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
||||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
||||||
:marked
|
:marked
|
||||||
@ -415,14 +418,14 @@ figure.image-display
|
|||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
||||||
That's surprising at first.
|
That's surprising at first.
|
||||||
|
|
||||||
Angular only calls the hook when the value of the input property changes.
|
Angular only calls the hook when the value of the input property changes.
|
||||||
The value of the `hero` property is the *reference to the hero object*.
|
The value of the `hero` property is the *reference to the hero object*.
|
||||||
Angular doesn't care that the hero's own `name` property changed.
|
Angular doesn't care that the hero's own `name` property changed.
|
||||||
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
@ -431,27 +434,27 @@ figure.image-display
|
|||||||
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
With this method we can detect a change that Angular overlooked.
|
With this method we can detect a change that Angular overlooked.
|
||||||
What we do with that information to refresh the display is a separate matter.
|
What we do with that information to refresh the display is a separate matter.
|
||||||
:marked
|
:marked
|
||||||
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
||||||
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We manually check everything that we care about, capturing and comparing against previous values.
|
We manually check everything that we care about, capturing and comparing against previous values.
|
||||||
We write a special message to the log when there are no substantive changes
|
We write a special message to the log when there are no substantive changes
|
||||||
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
||||||
|
|
||||||
The results are illuminating:
|
The results are illuminating:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
||||||
:marked
|
:marked
|
||||||
We now are able to detect when the hero's `name` has changed. But we must be careful.
|
We now are able to detect when the hero's `name` has changed. But we must be careful.
|
||||||
|
|
||||||
The `ngDoCheck` hook is called with enormous frequency —
|
The `ngDoCheck` hook is called with enormous frequency —
|
||||||
after _every_ change detection cycle no matter where the change occurred.
|
after _every_ change detection cycle no matter where the change occurred.
|
||||||
It's called over twenty times in this example before the user can do anything.
|
It's called over twenty times in this example before the user can do anything.
|
||||||
|
|
||||||
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
||||||
Mere mousing into another input box triggers a call.
|
Mere mousing into another input box triggers a call.
|
||||||
Relatively few calls reveal actual changes to pertinent data.
|
Relatively few calls reveal actual changes to pertinent data.
|
||||||
@ -459,7 +462,7 @@ figure.image-display
|
|||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We also see that the `ngOnChanges` method is called in contradiction of the
|
We also see that the `ngOnChanges` method is called in contradiction of the
|
||||||
[incorrect API documentation](../api/core/index/DoCheck-class.html).
|
[incorrect API documentation](../api/core/index/DoCheck-class.html).
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
@ -467,15 +470,15 @@ figure.image-display
|
|||||||
## AfterView
|
## AfterView
|
||||||
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
|
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
|
||||||
*after* it creates a component's child views.
|
*after* it creates a component's child views.
|
||||||
|
|
||||||
Here's a child view that displays a hero's name in an input box:
|
Here's a child view that displays a hero's name in an input box:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `AfterViewComponent` displays this child view *within its template*:
|
The `AfterViewComponent` displays this child view *within its template*:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The following hooks take action based on changing values *within the child view*
|
The following hooks take action based on changing values *within the child view*
|
||||||
which we can only reach by querying for the child view via the property decorated with
|
which we can only reach by querying for the child view via the property decorated with
|
||||||
[@ViewChild](../api/core/index/ViewChild-var.html).
|
[@ViewChild](../api/core/index/ViewChild-var.html).
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
||||||
@ -483,15 +486,15 @@ figure.image-display
|
|||||||
:marked
|
:marked
|
||||||
### Abide by the unidirectional data flow rule
|
### Abide by the unidirectional data flow rule
|
||||||
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
Why does the `doSomething` method wait a tick before updating `comment`?
|
Why does the `doSomething` method wait a tick before updating `comment`?
|
||||||
|
|
||||||
Because we must adhere to Angular's unidirectional data flow rule which says that
|
Because we must adhere to Angular's unidirectional data flow rule which says that
|
||||||
we may not update the view *after* it has been composed.
|
we may not update the view *after* it has been composed.
|
||||||
Both hooks fire after the component's view has been composed.
|
Both hooks fire after the component's view has been composed.
|
||||||
|
|
||||||
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
|
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
|
||||||
block tick-methods
|
block tick-methods
|
||||||
:marked
|
:marked
|
||||||
@ -510,19 +513,19 @@ figure.image-display
|
|||||||
## AfterContent
|
## AfterContent
|
||||||
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
|
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
|
||||||
*after* Angular projects external content into the component.
|
*after* Angular projects external content into the component.
|
||||||
|
|
||||||
### Content projection
|
### Content projection
|
||||||
*Content projection* is a way to import HTML content from outside the component and insert that content
|
*Content projection* is a way to import HTML content from outside the component and insert that content
|
||||||
into the component's template in a designated spot.
|
into the component's template in a designated spot.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Angular 1 developers know this technique as *transclusion*.
|
Angular 1 developers know this technique as *transclusion*.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We'll illustrate with a variation on the [previous](#afterview) example
|
We'll illustrate with a variation on the [previous](#afterview) example
|
||||||
whose behavior and output is almost the same.
|
whose behavior and output is almost the same.
|
||||||
|
|
||||||
This time, instead of including the child view within the template, we'll import it from
|
This time, instead of including the child view within the template, we'll import it from
|
||||||
the `AfterContentComponent`'s parent. Here's the parent's template.
|
the `AfterContentComponent`'s parent. Here's the parent's template.
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
|
||||||
@ -530,11 +533,11 @@ figure.image-display
|
|||||||
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
|
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
|
||||||
We never put content between a component's element tags *unless we intend to project that content
|
We never put content between a component's element tags *unless we intend to project that content
|
||||||
into the component*.
|
into the component*.
|
||||||
|
|
||||||
Now look at the component's template:
|
Now look at the component's template:
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The `<ng-content>` tag is a *placeholder* for the external content.
|
The `<ng-content>` tag is a *placeholder* for the external content.
|
||||||
They tell Angular where to insert that content.
|
They tell Angular where to insert that content.
|
||||||
In this case, the projected content is the `<my-child>` from the parent.
|
In this case, the projected content is the `<my-child>` from the parent.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
@ -547,26 +550,26 @@ figure.image-display
|
|||||||
:marked
|
:marked
|
||||||
### AfterContent hooks
|
### AfterContent hooks
|
||||||
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
||||||
that we're looking for.
|
that we're looking for.
|
||||||
|
|
||||||
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
||||||
appear *within* the component's template.
|
appear *within* the component's template.
|
||||||
|
|
||||||
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
|
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
|
||||||
projected into the component.
|
projected into the component.
|
||||||
|
|
||||||
The following *AfterContent* hooks take action based on changing values in a *content child*
|
The following *AfterContent* hooks take action based on changing values in a *content child*
|
||||||
which we can only reach by querying for it via the property decorated with
|
which we can only reach by querying for it via the property decorated with
|
||||||
[@ContentChild](../api/core/index/ContentChild-var.html).
|
[@ContentChild](../api/core/index/ContentChild-var.html).
|
||||||
|
|
||||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
### No unidirectional flow worries
|
### No unidirectional flow worries
|
||||||
|
|
||||||
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
||||||
There's no [need to wait](#wait-a-tick).
|
There's no [need to wait](#wait-a-tick).
|
||||||
|
|
||||||
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
||||||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||||
We still have a window of opportunity to modify that view.
|
We still have a window of opportunity to modify that view.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user