docs(lifecycle-hooks): Dart sync with TS updates (#2952)
* initial cache sync Also updating glossary since that was missed earlier. * minor updates to Dart and TS jade * final cache sync * feedback from Ward
This commit is contained in:
parent
0f64eb8535
commit
50f5a80db1
|
@ -14,5 +14,5 @@ block other-angular-subsystems
|
|||
|
||||
block tick-methods
|
||||
: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()` postpones the log update
|
||||
for one turn of the browser's update cycle ... and that's just long enough.
|
||||
|
|
|
@ -185,7 +185,7 @@ a#aot
|
|||
Angular has a rich data binding framework with a variety of data binding
|
||||
operations and supporting declaration syntax.
|
||||
|
||||
Read about the forms of binding in the [Template Syntax](!{docsLatest}/guide/template-syntax.html#data-binding) page:
|
||||
Read about the forms of binding in the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page:
|
||||
* [Interpolation](!{docsLatest}/guide/template-syntax.html#interpolation).
|
||||
* [Property binding](!{docsLatest}/guide/template-syntax.html#property-binding).
|
||||
* [Event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
|
||||
|
|
|
@ -12,14 +12,18 @@ figure
|
|||
Angular creates it, renders it, creates and renders its children,
|
||||
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
|
||||
|
||||
Angular offers **component lifecycle hooks**
|
||||
that give us visibility into these key moments and the ability to act when they occur.
|
||||
Angular offers **lifecycle hooks**
|
||||
that provide visibility into these key life moments and the ability to act when they occur.
|
||||
|
||||
We cover these hooks in this chapter and demonstrate how they work in code.
|
||||
|
||||
* [The lifecycle hooks](#hooks-overview)
|
||||
* [The hook-call sequence](#hook-sequence)
|
||||
* [Other Angular lifecycle hooks](#other-lifecycles)
|
||||
A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views.
|
||||
<br class="l-clear-both">
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
## Table of Contents
|
||||
* [Overview](#hooks-overview)
|
||||
* [Each hook's purpose and timing](#hooks-purpose-timing)
|
||||
* [Interfaces are optional (technically)](#interface-optional)
|
||||
* [Other Angular lifecycle hooks](#other-lifecycle-hooks)
|
||||
* [The lifecycle sample](#the-sample)
|
||||
* [All](#peek-a-boo)
|
||||
* [Spying OnInit and OnDestroy](#spy)
|
||||
|
@ -27,73 +31,105 @@ figure
|
|||
* [DoCheck](#docheck)
|
||||
* [AfterViewInit and AfterViewChecked](#afterview)
|
||||
* [AfterContentInit and AfterContentChecked](#aftercontent)
|
||||
|
||||
:marked
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
a#hooks-overview
|
||||
.l-main-section
|
||||
:marked
|
||||
## Component lifecycle Hooks
|
||||
## Component lifecycle hooks
|
||||
Directive and component instances have a lifecycle
|
||||
as Angular creates, updates, and destroys them.
|
||||
|
||||
Developers can tap into key moments in that lifecycle by implementing
|
||||
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`.
|
||||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||
We might implement it in a component class like this:
|
||||
For example, the `OnInit` interface has a hook method named `ngOnInit`
|
||||
that Angular calls shortly after creating the component:
|
||||
+makeExample('lifecycle-hooks/ts/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.')
|
||||
: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 the lifecycle hooks and some of the hooks only make sense for components.
|
||||
Angular only calls a directive/component hook method *if it is defined*.
|
||||
|
||||
+ifDocsFor('ts|js')
|
||||
.l-sub-section
|
||||
:marked
|
||||
### Interface optional?
|
||||
|
||||
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
||||
The JavaScript language doesn't have interfaces.
|
||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
||||
|
||||
Fortunately, they aren't necessary.
|
||||
We don't have to add the lifecycle hook interfaces to our directives and components to benefit from the hooks themselves.
|
||||
|
||||
Angular instead inspects our directive and component classes and calls the hook methods *if they are defined*.
|
||||
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
||||
|
||||
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
||||
in order to benefit from strong typing and editor tooling.
|
||||
|
||||
a#hooks-purpose-timing
|
||||
.l-main-section
|
||||
:marked
|
||||
Here are the component lifecycle hook methods:
|
||||
|
||||
### Directives and Components
|
||||
|
||||
## Lifecycle sequence
|
||||
*After* creating a component/directive by calling its constructor, Angular
|
||||
calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Purpose
|
||||
tr(style=top)
|
||||
td ngOnInit
|
||||
td
|
||||
:marked
|
||||
Initialize the directive/component after Angular initializes the data-bound input properties.
|
||||
th Purpose and Timing
|
||||
|
||||
tr(style=top)
|
||||
td ngOnChanges
|
||||
td
|
||||
:marked
|
||||
Respond after Angular sets a data-bound input property.
|
||||
The method receives a `changes` object of current and previous values.
|
||||
Respond when Angular (re)sets data-bound input properties.
|
||||
The method receives a `SimpleChanges` object of current and previous property values.
|
||||
|
||||
Called before `ngOnInit` and whenever one or more data-bound input properties change.
|
||||
|
||||
tr(style=top)
|
||||
td ngOnInit
|
||||
td
|
||||
:marked
|
||||
Initialize the directive/component after Angular first displays the data-bound properties
|
||||
and sets the directive/component's input properties.
|
||||
|
||||
Called _once_, after the _first_ `ngOnChanges`.
|
||||
|
||||
tr(style=top)
|
||||
td ngDoCheck
|
||||
td
|
||||
:marked
|
||||
Detect and act upon changes that Angular can't or won't
|
||||
detect on its own. Called every change detection run.
|
||||
Detect and act upon changes that Angular can't or won't detect on its own.
|
||||
|
||||
Called during every change detection run, immediately after `ngOnChanges` and `ngOnInit`.
|
||||
|
||||
tr(style=top)
|
||||
td ngAfterContentInit
|
||||
td
|
||||
:marked
|
||||
Respond after Angular projects external content into the component's view.
|
||||
|
||||
Called _once_ after the first `NgDoCheck`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
tr(style=top)
|
||||
td ngAfterContentChecked
|
||||
td
|
||||
:marked
|
||||
Respond after Angular checks the content projected into the component.
|
||||
|
||||
Called after the `ngAfterContentInit` and every subsequent `NgDoCheck`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
tr(style=top)
|
||||
td ngAfterViewInit
|
||||
td
|
||||
:marked
|
||||
Respond after Angular initializes the component's views and child views.
|
||||
|
||||
Called _once_ after the first `ngAfterContentChecked`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
tr(style=top)
|
||||
td ngAfterViewChecked
|
||||
td
|
||||
:marked
|
||||
Respond after Angular checks the component's views and child views.
|
||||
|
||||
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
tr(style=top)
|
||||
td ngOnDestroy
|
||||
td
|
||||
|
@ -101,103 +137,39 @@ table(width="100%")
|
|||
Cleanup just before Angular destroys the directive/component.
|
||||
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
||||
|
||||
:marked
|
||||
### Components only
|
||||
Called _just before_ Angular destroys the directive/component.
|
||||
|
||||
table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Purpose
|
||||
tr(style=top)
|
||||
td ngAfterContentInit
|
||||
td
|
||||
+ifDocsFor('ts|js')
|
||||
a#interface-optional
|
||||
.l-main-section
|
||||
:marked
|
||||
After Angular projects external content into its view.
|
||||
tr(style=top)
|
||||
td ngAfterContentChecked
|
||||
td
|
||||
:marked
|
||||
After Angular checks the bindings of the external content that it projected into its view.
|
||||
tr(style=top)
|
||||
td ngAfterViewInit
|
||||
td
|
||||
:marked
|
||||
After Angular creates the component's view(s).
|
||||
tr(style=top)
|
||||
td ngAfterViewChecked
|
||||
td
|
||||
:marked
|
||||
After Angular checks the bindings of the component's view(s).
|
||||
:marked
|
||||
Angular does not call the hook methods in this order.
|
||||
## Interface are optional (technically)
|
||||
|
||||
a(id="hook-sequence")
|
||||
.l-main-section
|
||||
:marked
|
||||
## Lifecycle sequence
|
||||
*After* Angular creates a component/directive by `new`-ing its constructor,
|
||||
it calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Timing
|
||||
tr(style=top)
|
||||
td ngOnChanges
|
||||
td
|
||||
:marked
|
||||
before `ngOnInit` and when a data-bound input property value changes.
|
||||
tr(style=top)
|
||||
td ngOnInit
|
||||
td
|
||||
:marked
|
||||
after the first `ngOnChanges`.
|
||||
tr(style=top)
|
||||
td ngDoCheck
|
||||
td
|
||||
:marked
|
||||
during every Angular change detection cycle.
|
||||
tr(style=top)
|
||||
td ngAfterContentInit
|
||||
td
|
||||
:marked
|
||||
after projecting content into the component.
|
||||
tr(style=top)
|
||||
td ngAfterContentChecked
|
||||
td
|
||||
:marked
|
||||
after every check of projected component content.
|
||||
tr(style=top)
|
||||
td ngAfterViewInit
|
||||
td
|
||||
:marked
|
||||
after initializing the component's views and child views.
|
||||
tr(style=top)
|
||||
td ngAfterViewChecked
|
||||
td
|
||||
:marked
|
||||
after every check of the component's views and child views.
|
||||
tr(style=top)
|
||||
td ngOnDestroy
|
||||
td
|
||||
:marked
|
||||
just before Angular destroys the directive/component.
|
||||
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
||||
The JavaScript language doesn't have interfaces.
|
||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
||||
|
||||
a(id="other-lifecycles")
|
||||
Fortunately, they aren't necessary.
|
||||
You don't have to add the lifecycle hook interfaces to directives and components to benefit from the hooks themselves.
|
||||
|
||||
Angular instead inspects directive and component classes and calls the hook methods *if they are defined*.
|
||||
Angular finds and calls methods like `ngOnInit()`, with or without the interfaces.
|
||||
|
||||
Nonetheless, it's good practice to add interfaces to TypeScript directive classes
|
||||
in order to benefit from strong typing and editor tooling.
|
||||
|
||||
a#other-lifecycle-hooks
|
||||
.l-main-section
|
||||
:marked
|
||||
## 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 these component hooks.
|
||||
|
||||
block other-angular-subsystems
|
||||
//- 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 developers more
|
||||
control over how these libraries are used.
|
||||
|
||||
.l-main-section#the-sample
|
||||
|
@ -230,10 +202,10 @@ table(width="100%")
|
|||
td
|
||||
:marked
|
||||
Directives have lifecycle hooks too.
|
||||
We create a `SpyDirective` that logs when the element it spies upon is
|
||||
A `SpyDirective` can log when the element it spies upon is
|
||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
||||
|
||||
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||
This example applies the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||
managed by the parent `SpyComponent`.
|
||||
tr(style=top)
|
||||
td <a href="#onchanges">OnChanges</a>
|
||||
|
@ -270,22 +242,22 @@ table(width="100%")
|
|||
|
||||
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
||||
every time the parent component increments its input counter property.
|
||||
Meanwhile, we apply the `SpyDirective` from the previous example
|
||||
to the `CounterComponent` log and watch log entries be created and destroyed.
|
||||
Meanwhile, the `SpyDirective` from the previous example is applied
|
||||
to the `CounterComponent` log where it watches log entries being created and destroyed.
|
||||
|
||||
:marked
|
||||
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
||||
The remainder of this chapter discusses selected exercises in further detail.
|
||||
|
||||
a(id="peek-a-boo")
|
||||
a#peek-a-boo
|
||||
.l-main-section
|
||||
:marked
|
||||
## Peek-a-boo: all hooks
|
||||
The `PeekABooComponent` demonstrates all of the hooks in one component.
|
||||
|
||||
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.
|
||||
You would rarely, if ever, implement all of the interfaces like this.
|
||||
The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
||||
|
||||
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
||||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
||||
:marked
|
||||
|
@ -296,135 +268,138 @@ figure.image-display
|
|||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.
|
||||
:marked
|
||||
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
||||
Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of
|
||||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
||||
as lean as possible!
|
||||
Clearly these three hooks fire a *often*. Keep the logic in these hooks as lean as possible!
|
||||
|
||||
Our next examples focus on hook details.
|
||||
The next examples focus on hook details.
|
||||
|
||||
.a(id="spy")
|
||||
a#spy
|
||||
.l-main-section
|
||||
:marked
|
||||
## Spying *OnInit* and *OnDestroy*
|
||||
|
||||
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.
|
||||
Go undercover with these two spy hooks to discover when an element is initialized or destroyed.
|
||||
|
||||
This is the perfect infiltration job for a directive.
|
||||
Our heroes will never know it's there.
|
||||
The heroes will never know they're being watched.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Kidding aside, we're emphasizing two key points:
|
||||
Kidding aside, pay attention to 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.<br><br>
|
||||
|
||||
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`.
|
||||
We can't modify a third party component either.
|
||||
But we can watch both with a directive.
|
||||
2. A spy directive can provide insight into a DOM object that you cannot change directly.
|
||||
Obviously you can't touch the implementation of a native `div`.
|
||||
You can't modify a third party component either.
|
||||
But you can watch both with a directive.
|
||||
|
||||
|
||||
:marked
|
||||
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
||||
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
||||
that log messages to the parent via an injected `LoggerService`.
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
||||
|
||||
:marked
|
||||
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||
You can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||
at the same time as that element.
|
||||
Here we attach it to the repeated hero `<div>`
|
||||
Here it is attached to the repeated hero `<div>`
|
||||
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
||||
|
||||
:marked
|
||||
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 seen here:
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
||||
|
||||
:marked
|
||||
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
||||
We see a new entry for each hero.
|
||||
|
||||
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 `<div>` elements from the DOM and destroys their spy directives at the same time.
|
||||
The spy's `ngOnDestroy` method reports its last moments.
|
||||
|
||||
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
||||
Let's see why we need them.
|
||||
|
||||
### OnInit
|
||||
|
||||
We turn to `ngOnInit` for two main reasons:
|
||||
1. To perform complex initializations shortly after construction
|
||||
1. To set up the component after Angular sets the input properties
|
||||
Use `ngOnInit` for two main reasons:
|
||||
1. to perform complex initializations shortly after construction
|
||||
1. to set up the component after Angular sets the input properties
|
||||
|
||||
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.
|
||||
|
||||
We don't fetch data in a component constructor. Why?
|
||||
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
|
||||
created under test or before we decide to display it.
|
||||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
When a component must start working _soon_ after creation,
|
||||
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
||||
That's where the heavy initialization logic belongs.
|
||||
|
||||
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.
|
||||
They'll have been set when our `ngOninit` runs.
|
||||
Experienced developers agree that components should be cheap and safe to construct.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Our first opportunity to access those properties is the `ngOnChanges` method which
|
||||
Angular calls before `ngOnInit`. But Angular calls `ngOnChanges` many times after that.
|
||||
Misko Hevery, Angular team lead,
|
||||
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
|
||||
you should avoid complex constructor logic.
|
||||
|
||||
:marked
|
||||
Don't fetch data in a component constructor.
|
||||
You shouldn't worry that a new component will try to contact a remote server when
|
||||
created under test or before you decide to display it.
|
||||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
An `ngOnInit` is a good place for a component to fetch its initial data. The
|
||||
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapter
|
||||
show how.
|
||||
|
||||
|
||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
||||
That's a problem if you need to initialize the directive based on those properties.
|
||||
They'll have been set when `ngOninit` runs.
|
||||
.l-sub-section
|
||||
:marked
|
||||
The `ngOnChanges` method is your first opportunity to access those properties.
|
||||
Angular calls `ngOnChanges` before `ngOnInit` ... and many times after that.
|
||||
It only calls `ngOnInit` once.
|
||||
:marked
|
||||
You can count on Angular to call the `ngOnInit` method _soon_ after creating the component.
|
||||
That's where the heavy initialization logic belongs.
|
||||
|
||||
### OnDestroy
|
||||
|
||||
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 the component is going away.
|
||||
|
||||
This is the place to free resources that won't be garbage collected automatically.
|
||||
Unsubscribe from observables and DOM events. Stop interval timers.
|
||||
Unregister all callbacks that this directive registered with global or application services.
|
||||
We risk memory leaks if we neglect to do so.
|
||||
You risk memory leaks if you neglect to do so.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## OnChanges
|
||||
|
||||
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).
|
||||
|
||||
Here is our implementation of the hook.
|
||||
This example monitors the `OnChanges` hook.
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
||||
:marked
|
||||
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.
|
||||
We iterate over the changed properties and log them.
|
||||
[SimpleChange](../api/core/index/SimpleChange-class.html) object holding the current and previous property values.
|
||||
This hook iterates over the changed properties and logs them.
|
||||
|
||||
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
||||
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
||||
:marked
|
||||
The parent binds to them like this:
|
||||
The host `OnChangesParentComponent` binds to them like this:
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes-parent.component.html', 'on-changes')
|
||||
:marked
|
||||
Here's the sample in action as we make changes.
|
||||
Here's the sample in action as the user makes changes.
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
||||
|
||||
:marked
|
||||
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
||||
The log entries appear as the string value of the *power* property changes.
|
||||
But the `ngOnChanges` does not catch changes to `hero.name`
|
||||
That's surprising at first.
|
||||
|
||||
Angular only calls the hook when the value of the input property changes.
|
||||
|
@ -435,39 +410,30 @@ figure.image-display
|
|||
.l-main-section
|
||||
:marked
|
||||
## DoCheck
|
||||
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||
Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
Use this method to detect a change that Angular overlooked.
|
||||
:marked
|
||||
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
||||
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck` hook:
|
||||
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
||||
:marked
|
||||
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
|
||||
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
||||
|
||||
The results are illuminating:
|
||||
This code inspects certain _values-of-interest_, capturing and comparing their current state against previous values.
|
||||
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
|
||||
so you can see how often `DoCheck` is called. The results are illuminating:
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
||||
:marked
|
||||
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 —
|
||||
While the `ngDoCheck` hook can detect when the hero's `name` has changed, it has a frightful cost.
|
||||
This hook is called with enormous frequency —
|
||||
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.
|
||||
|
||||
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.
|
||||
Relatively few calls reveal actual changes to pertinent data.
|
||||
Clearly our implementation must be very lightweight or the user experience may suffer.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We also see that the `ngOnChanges` method is called in contradiction of the
|
||||
[incorrect API documentation](../api/core/index/DoCheck-class.html).
|
||||
Clearly our implementation must be very lightweight or the user experience will suffer.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
@ -482,11 +448,11 @@ figure.image-display
|
|||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
||||
:marked
|
||||
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
|
||||
[@ViewChild](../api/core/index/ViewChild-var.html).
|
||||
which can only be reached by querying for the child view via the property decorated with
|
||||
[@ViewChild](../api/core/index/ViewChild-decorator.html).
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
||||
.a(id="wait-a-tick")
|
||||
#wait-a-tick
|
||||
:marked
|
||||
### Abide by the unidirectional data flow rule
|
||||
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
||||
|
@ -495,14 +461,14 @@ figure.image-display
|
|||
:marked
|
||||
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
|
||||
we may not update the view *after* it has been composed.
|
||||
Both hooks fire after the component's view has been composed.
|
||||
Angular's unidirectional data flow rule forbids updates to the view *after* it has been composed.
|
||||
Both of these 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 the hook updates the component's data-bound `comment` property immediately (try it!).
|
||||
block tick-methods
|
||||
:marked
|
||||
The `LoggerService.tick` methods, which are implemented by a call to `setTimeout`, postpone the update one turn of the of the browser's JavaScript cycle ... and that's long enough.
|
||||
The `LoggerService.tick_then()` postpones the log update
|
||||
for one turn of the browser's JavaScript cycle ... and that's just long enough.
|
||||
|
||||
:marked
|
||||
Here's *AfterView* in action
|
||||
|
@ -527,22 +493,20 @@ figure.image-display
|
|||
Angular 1 developers know this technique as *transclusion*.
|
||||
|
||||
:marked
|
||||
We'll illustrate with a variation on the [previous](#afterview) example
|
||||
whose behavior and output is almost the same.
|
||||
|
||||
This time, instead of including the child view within the template, we'll import it from
|
||||
Consider this variation on the [previous _AfterView_](#afterview) example.
|
||||
This time, instead of including the child view within the template, it imports the content from
|
||||
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=".")
|
||||
:marked
|
||||
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
|
||||
Never put content between a component's element tags *unless you intend to project that content
|
||||
into the component*.
|
||||
|
||||
Now look at the component's template:
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
||||
:marked
|
||||
The `<ng-content>` tag is a *placeholder* for the external content.
|
||||
They tell Angular where to insert that content.
|
||||
It tells Angular where to insert that content.
|
||||
In this case, the projected content is the `<my-child>` from the parent.
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content")
|
||||
|
@ -553,8 +517,8 @@ figure.image-display
|
|||
and (b) the presence of `<ng-content>` tags in the component's template.
|
||||
:marked
|
||||
### AfterContent hooks
|
||||
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
||||
that we're looking for.
|
||||
*AfterContent* hooks are similar to the *AfterView* hooks.
|
||||
The key difference is in the child component
|
||||
|
||||
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
||||
appear *within* the component's template.
|
||||
|
@ -563,17 +527,18 @@ figure.image-display
|
|||
projected into the component.
|
||||
|
||||
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
|
||||
[@ContentChild](../api/core/index/ContentChild-var.html).
|
||||
which can only be reached by querying for it via the property decorated with
|
||||
[@ContentChild](../api/core/index/ContentChild-decorator.html).
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
||||
|
||||
a#no-unidirectional-flow-worries
|
||||
:marked
|
||||
### No unidirectional flow worries
|
||||
### No unidirectional flow worries with _AfterContent..._
|
||||
|
||||
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
||||
There's no [need to wait](#wait-a-tick).
|
||||
|
||||
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.
|
||||
We still have a window of opportunity to modify that view.
|
||||
There is a small window between the `AfterContent...` and `AfterView...` hooks to modify the host view.
|
||||
|
|
|
@ -17,16 +17,13 @@ figure
|
|||
|
||||
A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views.
|
||||
<br class="l-clear-both">
|
||||
## Table of Contents
|
||||
* [Overview](#hooks-overview)
|
||||
<br><br>
|
||||
* [Each hook's purpose and timing](#hooks-purpose-timing)
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
## Table of Contents
|
||||
* [Overview](#hooks-overview)
|
||||
* [Each hook's purpose and timing](#hooks-purpose-timing)
|
||||
* [Interfaces are optional (technically)](#interface-optional)
|
||||
:marked
|
||||
* [Other Angular lifecycle hooks](#other-lifecycle-hooks)
|
||||
<br><br>
|
||||
* [The lifecycle sample](#the-sample)
|
||||
* [All](#peek-a-boo)
|
||||
* [Spying OnInit and OnDestroy](#spy)
|
||||
|
@ -40,7 +37,7 @@ figure
|
|||
a#hooks-overview
|
||||
.l-main-section
|
||||
:marked
|
||||
## Component lifecycle Hooks
|
||||
## Component lifecycle hooks
|
||||
Directive and component instances have a lifecycle
|
||||
as Angular creates, updates, and destroys them.
|
||||
Developers can tap into key moments in that lifecycle by implementing
|
||||
|
@ -58,8 +55,8 @@ a#hooks-purpose-timing
|
|||
.l-main-section
|
||||
:marked
|
||||
## Lifecycle sequence
|
||||
*After* Angular creates a component/directive by `new`-ing its constructor,
|
||||
it calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
*After* creating a component/directive by calling its constructor, Angular
|
||||
calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
|
@ -535,6 +532,7 @@ figure.image-display
|
|||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
||||
|
||||
a#no-unidirectional-flow-worries
|
||||
:marked
|
||||
### No unidirectional flow worries with _AfterContent..._
|
||||
|
||||
|
|
Loading…
Reference in New Issue