docs(lifecycle): update docs for lifecycle hooks
This commit is contained in:
parent
0b9425bbb4
commit
d299ce4bcf
|
@ -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: `<p>myProp = {{myProp}}</p>`
|
||||
* })
|
||||
* class MyComponent implements OnChanges {
|
||||
* @Input() myProp: any;
|
||||
*
|
||||
* ngOnChanges(changes: SimpleChanges) {
|
||||
* console.log('ngOnChanges - myProp = ' + changes['myProp'].currentValue);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="value = value + 1">Change MyComponent</button>
|
||||
* <my-cmp [my-prop]="value"></my-cmp>`,
|
||||
* 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: `<p>my-component</p>`
|
||||
* })
|
||||
* class MyComponent implements OnInit, OnDestroy {
|
||||
* ngOnInit() {
|
||||
* console.log('ngOnInit');
|
||||
* }
|
||||
*
|
||||
* ngOnDestroy() {
|
||||
* console.log('ngOnDestroy');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="hasChild = !hasChild">
|
||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
||||
* </button>
|
||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
||||
* 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: `
|
||||
* <p>Changes:</p>
|
||||
* <ul>
|
||||
* <li *ngFor="let line of logs">{{line}}</li>
|
||||
* </ul>`,
|
||||
* 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: `
|
||||
* <button (click)="list.push(list.length)">Push</button>
|
||||
* <button (click)="list.pop()">Pop</button>
|
||||
* <custom-check [list]="list"></custom-check>`,
|
||||
* 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: `<p>my-component</p>`
|
||||
* })
|
||||
* class MyComponent implements OnInit, OnDestroy {
|
||||
* ngOnInit() {
|
||||
* console.log('ngOnInit');
|
||||
* }
|
||||
*
|
||||
* ngOnDestroy() {
|
||||
* console.log('ngOnDestroy');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <button (click)="hasChild = !hasChild">
|
||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
||||
* </button>
|
||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
||||
* 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: `<ng-content></ng-content>`
|
||||
* })
|
||||
* 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: `
|
||||
* <parent-cmp>
|
||||
* <child-cmp where="content"></child-cmp>
|
||||
* </parent-cmp>`,
|
||||
* 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: `<ng-content></ng-content>`})
|
||||
* 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: `
|
||||
* <parent-cmp>
|
||||
* <button (click)="hasContent = !hasContent">Toggle content child</button>
|
||||
* <child-cmp *ngIf="hasContent" where="content"></child-cmp>
|
||||
* </parent-cmp>`,
|
||||
* 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: `<child-cmp where="view"></child-cmp>`,
|
||||
* 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: `<parent-cmp></parent-cmp>`,
|
||||
* 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: `
|
||||
* <button (click)="showView = !showView">Toggle view child</button>
|
||||
* <child-cmp *ngIf="showView" where="view"></child-cmp>`,
|
||||
* 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: `<parent-cmp></parent-cmp>`,
|
||||
* directives: [ParentComponent]
|
||||
* })
|
||||
* export class App {
|
||||
* }
|
||||
* ```
|
||||
* @stable
|
||||
*/
|
||||
export abstract class AfterViewChecked { abstract ngAfterViewChecked(): void; }
|
||||
|
|
|
@ -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<any>, inputs: string[] = []): any[] {
|
||||
const log: any[] = [];
|
||||
createLoggingSpiesFromProto(clazz, log);
|
||||
|
||||
const inputBindings = inputs.map(input => `[${input}] = true`).join(' ');
|
||||
|
||||
@Component({template: `<my-cmp ${inputBindings}></my-cmp>`})
|
||||
class ParentComponent {
|
||||
}
|
||||
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [ParentComponent, clazz]})
|
||||
.createComponent(ParentComponent);
|
||||
fixture.detectChanges();
|
||||
fixture.destroy();
|
||||
return log;
|
||||
}
|
||||
|
||||
function createLoggingSpiesFromProto(clazz: Type<any>, log: any[]) {
|
||||
const proto = clazz.prototype;
|
||||
Object.keys(proto).forEach((method) => {
|
||||
proto[method] = (...args: any[]) => { log.push([method, args]); };
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue