diff --git a/modules/@angular/core/src/metadata/lifecycle_hooks.ts b/modules/@angular/core/src/metadata/lifecycle_hooks.ts
index 27f35983b7..594bcc63fe 100644
--- a/modules/@angular/core/src/metadata/lifecycle_hooks.ts
+++ b/modules/@angular/core/src/metadata/lifecycle_hooks.ts
@@ -36,447 +36,123 @@ export var LIFECYCLE_HOOKS_VALUES = [
];
/**
- * Lifecycle hooks are guaranteed to be called in the following order:
- * - `OnChanges` (if any bindings have changed),
- * - `OnInit` (after the first check only),
- * - `DoCheck`,
- * - `AfterContentInit`,
- * - `AfterContentChecked`,
- * - `AfterViewInit`,
- * - `AfterViewChecked`,
- * - `OnDestroy` (at the very end before destruction)
- */
-
-/**
- * Implement this interface to get notified when any data-bound property of your directive changes.
+ * @whatItDoes Lifecycle hook that is called when any data-bound property of a directive changes.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnChanges'}
*
+ * @description
* `ngOnChanges` is called right after the data-bound properties have been checked and before view
* and content children are checked if at least one of them has changed.
+ * The `changes` parameter contains the changed properties.
*
- * The `changes` parameter contains an entry for each of the changed data-bound property. The key is
- * the property name and the value is an instance of {@link SimpleChange}.
+ * See {@linkDocs guide/lifecycle-hooks#onchanges "Lifecycle Hooks Guide"}.
*
- * ### Example ([live example](http://plnkr.co/edit/AHrB6opLqHDBPkt4KpdT?p=preview)):
- *
- * ```typescript
- * @Component({
- * selector: 'my-cmp',
- * template: `
myProp = {{myProp}}
`
- * })
- * class MyComponent implements OnChanges {
- * @Input() myProp: any;
- *
- * ngOnChanges(changes: SimpleChanges) {
- * console.log('ngOnChanges - myProp = ' + changes['myProp'].currentValue);
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- * `,
- * directives: [MyComponent]
- * })
- * export class App {
- * value = 0;
- * }
- * ```
* @stable
*/
export abstract class OnChanges { abstract ngOnChanges(changes: SimpleChanges): void; }
/**
- * Implement this interface to execute custom initialization logic after your directive's
- * data-bound properties have been initialized.
+ * @whatItDoes Lifecycle hook that is called after data-bound properties of a directive are
+ * initialized.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnInit'}
*
+ * @description
* `ngOnInit` is called right after the directive's data-bound properties have been checked for the
* first time, and before any of its children have been checked. It is invoked only once when the
* directive is instantiated.
*
- * ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
+ * See {@linkDocs guide/lifecycle-hooks "Lifecycle Hooks Guide"}.
*
- * ```typescript
- * @Component({
- * selector: 'my-cmp',
- * template: `
my-component
`
- * })
- * class MyComponent implements OnInit, OnDestroy {
- * ngOnInit() {
- * console.log('ngOnInit');
- * }
- *
- * ngOnDestroy() {
- * console.log('ngOnDestroy');
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- * `,
- * directives: [MyComponent, NgIf]
- * })
- * export class App {
- * hasChild = true;
- * }
- * ```
* @stable
*/
export abstract class OnInit { abstract ngOnInit(): void; }
/**
- * Implement this interface to supplement the default change detection algorithm in your directive.
+ * @whatItDoes Lifecycle hook that is called when Angular dirty checks a directive.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='DoCheck'}
*
+ * @description
* `ngDoCheck` gets called to check the changes in the directives in addition to the default
- * algorithm.
- *
- * The default change detection algorithm looks for differences by comparing bound-property values
- * by reference across change detection runs.
+ * algorithm. The default change detection algorithm looks for differences by comparing
+ * bound-property values by reference across change detection runs.
*
* Note that a directive typically should not use both `DoCheck` and {@link OnChanges} to respond to
- * changes on the same input. `ngOnChanges` will continue to be called when the default change
- * detector
- * detects changes, so it is usually unnecessary to respond to changes on the same input in both
- * hooks.
- * Reaction to the changes have to be handled from within the `ngDoCheck` callback.
+ * changes on the same input, as `ngOnChanges` will continue to be called when the default change
+ * detector detects changes.
*
- * You can use {@link KeyValueDiffers} and {@link IterableDiffers} to help add your custom check
- * mechanisms.
+ * See {@link KeyValueDiffers} and {@link IterableDiffers} for implementing custom dirty checking
+ * for collections.
*
- * ### Example ([live demo](http://plnkr.co/edit/QpnIlF0CR2i5bcYbHEUJ?p=preview))
+ * See {@linkDocs guide/lifecycle-hooks#docheck "Lifecycle Hooks Guide"}.
*
- * In the following example `ngDoCheck` uses an {@link IterableDiffers} to detect the updates to the
- * array `list`:
- *
- * ```typescript
- * @Component({
- * selector: 'custom-check',
- * template: `
- *
Changes:
- *
- *
{{line}}
- *
`,
- * directives: [NgFor]
- * })
- * class CustomCheckComponent implements DoCheck {
- * @Input() list: any[];
- * differ: any;
- * logs = [];
- *
- * constructor(differs: IterableDiffers) {
- * this.differ = differs.find([]).create(null);
- * }
- *
- * ngDoCheck() {
- * var changes = this.differ.diff(this.list);
- *
- * if (changes) {
- * changes.forEachAddedItem(r => this.logs.push('added ' + r.item));
- * changes.forEachRemovedItem(r => this.logs.push('removed ' + r.item))
- * }
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- *
- * `,
- * directives: [CustomCheckComponent]
- * })
- * export class App {
- * list = [];
- * }
- * ```
* @stable
*/
export abstract class DoCheck { abstract ngDoCheck(): void; }
/**
- * Implement this interface to get notified when your directive is destroyed.
+ * @whatItDoes Lifecycle hook that is called when a directive or pipe is destroyed.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnDestroy'}
*
+ * @description
* `ngOnDestroy` callback is typically used for any custom cleanup that needs to occur when the
- * instance is destroyed
+ * instance is destroyed.
*
- * ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
- *
- * ```typesript
- * @Component({
- * selector: 'my-cmp',
- * template: `
my-component
`
- * })
- * class MyComponent implements OnInit, OnDestroy {
- * ngOnInit() {
- * console.log('ngOnInit');
- * }
- *
- * ngOnDestroy() {
- * console.log('ngOnDestroy');
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- * `,
- * directives: [MyComponent, NgIf]
- * })
- * export class App {
- * hasChild = true;
- * }
- * ```
- *
- *
- * To create a stateful Pipe, you should implement this interface and set the `pure`
- * parameter to `false` in the {@link Pipe}.
- *
- * A stateful pipe may produce different output, given the same input. It is
- * likely that a stateful pipe may contain state that should be cleaned up when
- * a binding is destroyed. For example, a subscription to a stream of data may need to
- * be disposed, or an interval may need to be cleared.
- *
- * ### Example ([live demo](http://plnkr.co/edit/i8pm5brO4sPaLxBx56MR?p=preview))
- *
- * In this example, a pipe is created to countdown its input value, updating it every
- * 50ms. Because it maintains an internal interval, it automatically clears
- * the interval when the binding is destroyed or the countdown completes.
- *
- * ```
- * import {OnDestroy, Pipe, PipeTransform} from '@angular/core'
- * @Pipe({name: 'countdown', pure: false})
- * class CountDown implements PipeTransform, OnDestroy {
- * remainingTime:Number;
- * interval:SetInterval;
- * ngOnDestroy() {
- * if (this.interval) {
- * clearInterval(this.interval);
- * }
- * }
- * transform(value: any, args: any[] = []) {
- * if (!parseInt(value, 10)) return null;
- * if (typeof this.remainingTime !== 'number') {
- * this.remainingTime = parseInt(value, 10);
- * }
- * if (!this.interval) {
- * this.interval = setInterval(() => {
- * this.remainingTime-=50;
- * if (this.remainingTime <= 0) {
- * this.remainingTime = 0;
- * clearInterval(this.interval);
- * delete this.interval;
- * }
- * }, 50);
- * }
- * return this.remainingTime;
- * }
- * }
- * ```
- *
- * Invoking `{{ 10000 | countdown }}` would cause the value to be decremented by 50,
- * every 50ms, until it reaches 0.
+ * See {@linkDocs guide/lifecycle-hooks "Lifecycle Hooks Guide"}.
*
* @stable
*/
export abstract class OnDestroy { abstract ngOnDestroy(): void; }
/**
- * Implement this interface to get notified when your directive's content has been fully
+ *
+ * @whatItDoes Lifecycle hook that is called after a directive's content has been fully
* initialized.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentInit'}
*
- * ### Example ([live demo](http://plnkr.co/edit/plamXUpsLQbIXpViZhUO?p=preview))
+ * @description
+ * See {@linkDocs guide/lifecycle-hooks#aftercontent "Lifecycle Hooks Guide"}.
*
- * ```typescript
- * @Component({
- * selector: 'child-cmp',
- * template: `{{where}} child`
- * })
- * class ChildComponent {
- * @Input() where: string;
- * }
- *
- * @Component({
- * selector: 'parent-cmp',
- * template: ``
- * })
- * class ParentComponent implements AfterContentInit {
- * @ContentChild(ChildComponent) contentChild: ChildComponent;
- *
- * constructor() {
- * // contentChild is not initialized yet
- * console.log(this.getMessage(this.contentChild));
- * }
- *
- * ngAfterContentInit() {
- * // contentChild is updated after the content has been checked
- * console.log('AfterContentInit: ' + this.getMessage(this.contentChild));
- * }
- *
- * private getMessage(cmp: ChildComponent): string {
- * return cmp ? cmp.where + ' child' : 'no child';
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- *
- * `,
- * directives: [ParentComponent, ChildComponent]
- * })
- * export class App {
- * }
- * ```
* @stable
*/
export abstract class AfterContentInit { abstract ngAfterContentInit(): void; }
/**
- * Implement this interface to get notified after every check of your directive's content.
+ * @whatItDoes Lifecycle hook that is called after every check of a directive's content.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentChecked'}
*
- * ### Example ([live demo](http://plnkr.co/edit/tGdrytNEKQnecIPkD7NU?p=preview))
+ * @description
+ * See {@linkDocs guide/lifecycle-hooks#aftercontent "Lifecycle Hooks Guide"}.
*
- * ```typescript
- * @Component({selector: 'child-cmp', template: `{{where}} child`})
- * class ChildComponent {
- * @Input() where: string;
- * }
- *
- * @Component({selector: 'parent-cmp', template: ``})
- * class ParentComponent implements AfterContentChecked {
- * @ContentChild(ChildComponent) contentChild: ChildComponent;
- *
- * constructor() {
- * // contentChild is not initialized yet
- * console.log(this.getMessage(this.contentChild));
- * }
- *
- * ngAfterContentChecked() {
- * // contentChild is updated after the content has been checked
- * console.log('AfterContentChecked: ' + this.getMessage(this.contentChild));
- * }
- *
- * private getMessage(cmp: ChildComponent): string {
- * return cmp ? cmp.where + ' child' : 'no child';
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: `
- *
- *
- *
- * `,
- * directives: [NgIf, ParentComponent, ChildComponent]
- * })
- * export class App {
- * hasContent = true;
- * }
- * ```
* @stable
*/
export abstract class AfterContentChecked { abstract ngAfterContentChecked(): void; }
/**
- * Implement this interface to get notified when your component's view has been fully initialized.
+ * @whatItDoes Lifecycle hook that is called after a component's view has been fully
+ * initialized.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewInit'}
*
- * ### Example ([live demo](http://plnkr.co/edit/LhTKVMEM0fkJgyp4CI1W?p=preview))
+ * @description
+ * See {@linkDocs guide/lifecycle-hooks#afterview "Lifecycle Hooks Guide"}.
*
- * ```typescript
- * @Component({selector: 'child-cmp', template: `{{where}} child`})
- * class ChildComponent {
- * @Input() where: string;
- * }
- *
- * @Component({
- * selector: 'parent-cmp',
- * template: ``,
- * directives: [ChildComponent]
- * })
- * class ParentComponent implements AfterViewInit {
- * @ViewChild(ChildComponent) viewChild: ChildComponent;
- *
- * constructor() {
- * // viewChild is not initialized yet
- * console.log(this.getMessage(this.viewChild));
- * }
- *
- * ngAfterViewInit() {
- * // viewChild is updated after the view has been initialized
- * console.log('ngAfterViewInit: ' + this.getMessage(this.viewChild));
- * }
- *
- * private getMessage(cmp: ChildComponent): string {
- * return cmp ? cmp.where + ' child' : 'no child';
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: ``,
- * directives: [ParentComponent]
- * })
- * export class App {
- * }
- * ```
* @stable
*/
export abstract class AfterViewInit { abstract ngAfterViewInit(): void; }
/**
- * Implement this interface to get notified after every check of your component's view.
+ * @whatItDoes Lifecycle hook that is called after every check of a component's view.
+ * @howToUse
+ * {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewChecked'}
*
- * ### Example ([live demo](http://plnkr.co/edit/0qDGHcPQkc25CXhTNzKU?p=preview))
+ * @description
+ * See {@linkDocs guide/lifecycle-hooks#afterview "Lifecycle Hooks Guide"}.
*
- * ```typescript
- * @Component({selector: 'child-cmp', template: `{{where}} child`})
- * class ChildComponent {
- * @Input() where: string;
- * }
- *
- * @Component({
- * selector: 'parent-cmp',
- * template: `
- *
- * `,
- * directives: [NgIf, ChildComponent]
- * })
- * class ParentComponent implements AfterViewChecked {
- * @ViewChild(ChildComponent) viewChild: ChildComponent;
- * showView = true;
- *
- * constructor() {
- * // viewChild is not initialized yet
- * console.log(this.getMessage(this.viewChild));
- * }
- *
- * ngAfterViewChecked() {
- * // viewChild is updated after the view has been checked
- * console.log('AfterViewChecked: ' + this.getMessage(this.viewChild));
- * }
- *
- * private getMessage(cmp: ChildComponent): string {
- * return cmp ? cmp.where + ' child' : 'no child';
- * }
- * }
- *
- * @Component({
- * selector: 'app',
- * template: ``,
- * directives: [ParentComponent]
- * })
- * export class App {
- * }
- * ```
* @stable
*/
export abstract class AfterViewChecked { abstract ngAfterViewChecked(): void; }
diff --git a/modules/@angular/examples/core/ts/metadata/lifecycle_hooks_spec.ts b/modules/@angular/examples/core/ts/metadata/lifecycle_hooks_spec.ts
new file mode 100644
index 0000000000..76bfd99358
--- /dev/null
+++ b/modules/@angular/examples/core/ts/metadata/lifecycle_hooks_spec.ts
@@ -0,0 +1,150 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, DoCheck, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, Type} from '@angular/core';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
+
+export function main() {
+ describe('lifecycle hooks examples', () => {
+ it('should work with ngOnInit', () => {
+ // #docregion OnInit
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements OnInit {
+ ngOnInit() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngOnInit', []]]);
+ });
+
+ it('should work with ngDoCheck', () => {
+ // #docregion DoCheck
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements DoCheck {
+ ngDoCheck() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngDoCheck', []]]);
+ });
+
+ it('should work with ngAfterContentChecked', () => {
+ // #docregion AfterContentChecked
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements AfterContentChecked {
+ ngAfterContentChecked() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentChecked', []]]);
+ });
+
+ it('should work with ngAfterContentInit', () => {
+ // #docregion AfterContentInit
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements AfterContentInit {
+ ngAfterContentInit() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentInit', []]]);
+ });
+
+ it('should work with ngAfterViewChecked', () => {
+ // #docregion AfterViewChecked
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements AfterViewChecked {
+ ngAfterViewChecked() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewChecked', []]]);
+ });
+
+ it('should work with ngAfterViewInit', () => {
+ // #docregion AfterViewInit
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements AfterViewInit {
+ ngAfterViewInit() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewInit', []]]);
+ });
+
+ it('should work with ngOnDestroy', () => {
+ // #docregion OnDestroy
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements OnDestroy {
+ ngOnDestroy() {
+ // ...
+ }
+ }
+ // #enddocregion
+
+ expect(createAndLogComponent(MyComponent)).toEqual([['ngOnDestroy', []]]);
+ });
+
+ it('should work with ngOnChanges', () => {
+ // #docregion OnChanges
+ @Component({selector: 'my-cmp', template: `...`})
+ class MyComponent implements OnChanges {
+ @Input()
+ prop: number;
+
+ ngOnChanges(changes: SimpleChanges) {
+ // changes.prop contains the old and the new value...
+ }
+ }
+ // #enddocregion
+
+ const log = createAndLogComponent(MyComponent, ['prop']);
+ expect(log.length).toBe(1);
+ expect(log[0][0]).toBe('ngOnChanges');
+ const changes: SimpleChanges = log[0][1][0];
+ expect(changes['prop'].currentValue).toBe(true);
+ });
+ });
+
+ function createAndLogComponent(clazz: Type, inputs: string[] = []): any[] {
+ const log: any[] = [];
+ createLoggingSpiesFromProto(clazz, log);
+
+ const inputBindings = inputs.map(input => `[${input}] = true`).join(' ');
+
+ @Component({template: ``})
+ class ParentComponent {
+ }
+
+
+ const fixture = TestBed.configureTestingModule({declarations: [ParentComponent, clazz]})
+ .createComponent(ParentComponent);
+ fixture.detectChanges();
+ fixture.destroy();
+ return log;
+ }
+
+ function createLoggingSpiesFromProto(clazz: Type, log: any[]) {
+ const proto = clazz.prototype;
+ Object.keys(proto).forEach((method) => {
+ proto[method] = (...args: any[]) => { log.push([method, args]); };
+ });
+ }
+}