diff --git a/packages/core/test/acceptance/lifecycle_spec.ts b/packages/core/test/acceptance/lifecycle_spec.ts
index 611876f0fd..5175d3dd7d 100644
--- a/packages/core/test/acceptance/lifecycle_spec.ts
+++ b/packages/core/test/acceptance/lifecycle_spec.ts
@@ -3376,3 +3376,335 @@ describe('onDestroy', () => {
expect(events).toEqual(['dir']);
});
});
+
+describe('hook order', () => {
+ let events: string[] = [];
+
+ beforeEach(() => events = []);
+
+ @Component({
+ selector: 'comp',
+ template: `{{value}}
`,
+ })
+ class Comp {
+ @Input()
+ value = '';
+
+ @Input()
+ name = '';
+
+ ngOnInit() { events.push(`${this.name} onInit`); }
+
+ ngDoCheck() { events.push(`${this.name} doCheck`); }
+
+ ngOnChanges() { events.push(`${this.name} onChanges`); }
+
+ ngAfterContentInit() { events.push(`${this.name} afterContentInit`); }
+
+ ngAfterContentChecked() { events.push(`${this.name} afterContentChecked`); }
+
+ ngAfterViewInit() { events.push(`${this.name} afterViewInit`); }
+
+ ngAfterViewChecked() { events.push(`${this.name} afterViewChecked`); }
+
+ ngOnDestroy() { events.push(`${this.name} onDestroy`); }
+ }
+
+ @Component({
+ selector: 'parent',
+ template:
+ ``,
+ })
+ class Parent extends Comp {
+ }
+
+ it('should call all hooks in correct order', () => {
+ @Component({template: ``})
+ class App {
+ value = 'a';
+
+ show = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, Comp],
+ imports: [CommonModule],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'comp onChanges',
+ 'comp onInit',
+ 'comp doCheck',
+ 'comp afterContentInit',
+ 'comp afterContentChecked',
+ 'comp afterViewInit',
+ 'comp afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.detectChanges();
+ expect(events).toEqual([
+ 'comp doCheck',
+ 'comp afterContentChecked',
+ 'comp afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.value = 'b';
+ fixture.detectChanges();
+ expect(events).toEqual([
+ 'comp onChanges',
+ 'comp doCheck',
+ 'comp afterContentChecked',
+ 'comp afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.show = false;
+ fixture.detectChanges();
+ expect(events).toEqual([
+ 'comp onDestroy',
+ ]);
+ });
+
+ it('should call all hooks in correct order with children', () => {
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ value = 'a';
+
+ show = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, Parent, Comp],
+ imports: [CommonModule],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'parent1 onChanges',
+ 'parent1 onInit',
+ 'parent1 doCheck',
+ 'parent2 onChanges',
+ 'parent2 onInit',
+ 'parent2 doCheck',
+ 'parent1 afterContentInit',
+ 'parent1 afterContentChecked',
+ 'parent2 afterContentInit',
+ 'parent2 afterContentChecked',
+ 'child of parent1 onChanges',
+ 'child of parent1 onInit',
+ 'child of parent1 doCheck',
+ 'child of parent1 afterContentInit',
+ 'child of parent1 afterContentChecked',
+ 'child of parent1 afterViewInit',
+ 'child of parent1 afterViewChecked',
+ 'child of parent2 onChanges',
+ 'child of parent2 onInit',
+ 'child of parent2 doCheck',
+ 'child of parent2 afterContentInit',
+ 'child of parent2 afterContentChecked',
+ 'child of parent2 afterViewInit',
+ 'child of parent2 afterViewChecked',
+ 'parent1 afterViewInit',
+ 'parent1 afterViewChecked',
+ 'parent2 afterViewInit',
+ 'parent2 afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.value = 'b';
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'parent1 onChanges',
+ 'parent1 doCheck',
+ 'parent2 onChanges',
+ 'parent2 doCheck',
+ 'parent1 afterContentChecked',
+ 'parent2 afterContentChecked',
+ 'child of parent1 onChanges',
+ 'child of parent1 doCheck',
+ 'child of parent1 afterContentChecked',
+ 'child of parent1 afterViewChecked',
+ 'child of parent2 onChanges',
+ 'child of parent2 doCheck',
+ 'child of parent2 afterContentChecked',
+ 'child of parent2 afterViewChecked',
+ 'parent1 afterViewChecked',
+ 'parent2 afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.show = false;
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'child of parent1 onDestroy',
+ 'child of parent2 onDestroy',
+ 'parent1 onDestroy',
+ 'parent2 onDestroy',
+ ]);
+ });
+
+ // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-ng
+ it('should call all hooks in correct order with view and content', () => {
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ value = 'a';
+ show = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, Parent, Comp],
+ imports: [CommonModule],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'parent1 onChanges',
+ 'parent1 onInit',
+ 'parent1 doCheck',
+ 'projected1 onChanges',
+ 'projected1 onInit',
+ 'projected1 doCheck',
+ 'parent2 onChanges',
+ 'parent2 onInit',
+ 'parent2 doCheck',
+ 'projected2 onChanges',
+ 'projected2 onInit',
+ 'projected2 doCheck',
+ 'projected1 afterContentInit',
+ 'projected1 afterContentChecked',
+ 'parent1 afterContentInit',
+ 'parent1 afterContentChecked',
+ 'projected2 afterContentInit',
+ 'projected2 afterContentChecked',
+ 'parent2 afterContentInit',
+ 'parent2 afterContentChecked',
+ 'child of parent1 onChanges',
+ 'child of parent1 onInit',
+ 'child of parent1 doCheck',
+ 'child of parent1 afterContentInit',
+ 'child of parent1 afterContentChecked',
+ 'child of parent1 afterViewInit',
+ 'child of parent1 afterViewChecked',
+ 'child of parent2 onChanges',
+ 'child of parent2 onInit',
+ 'child of parent2 doCheck',
+ 'child of parent2 afterContentInit',
+ 'child of parent2 afterContentChecked',
+ 'child of parent2 afterViewInit',
+ 'child of parent2 afterViewChecked',
+ 'projected1 afterViewInit',
+ 'projected1 afterViewChecked',
+ 'parent1 afterViewInit',
+ 'parent1 afterViewChecked',
+ 'projected2 afterViewInit',
+ 'projected2 afterViewChecked',
+ 'parent2 afterViewInit',
+ 'parent2 afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.value = 'b';
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'parent1 onChanges',
+ 'parent1 doCheck',
+ 'projected1 onChanges',
+ 'projected1 doCheck',
+ 'parent2 onChanges',
+ 'parent2 doCheck',
+ 'projected2 onChanges',
+ 'projected2 doCheck',
+ 'projected1 afterContentChecked',
+ 'parent1 afterContentChecked',
+ 'projected2 afterContentChecked',
+ 'parent2 afterContentChecked',
+ 'child of parent1 onChanges',
+ 'child of parent1 doCheck',
+ 'child of parent1 afterContentChecked',
+ 'child of parent1 afterViewChecked',
+ 'child of parent2 onChanges',
+ 'child of parent2 doCheck',
+ 'child of parent2 afterContentChecked',
+ 'child of parent2 afterViewChecked',
+ 'projected1 afterViewChecked',
+ 'parent1 afterViewChecked',
+ 'projected2 afterViewChecked',
+ 'parent2 afterViewChecked',
+ ]);
+
+ events.length = 0;
+ fixture.componentInstance.show = false;
+ fixture.detectChanges();
+
+ expect(events).toEqual([
+ 'child of parent1 onDestroy',
+ 'child of parent2 onDestroy',
+ 'projected1 onDestroy',
+ 'parent1 onDestroy',
+ 'projected2 onDestroy',
+ 'parent2 onDestroy',
+ ]);
+ });
+});
+
+describe('non-regression', () => {
+ it('should call lifecycle hooks for directives active on ', () => {
+ let destroyed = false;
+
+ @Directive({
+ selector: '[onDestroyDir]',
+ })
+ class OnDestroyDir {
+ ngOnDestroy() { destroyed = true; }
+ }
+
+ @Component({
+ template: `
+ content
+ `
+ })
+ class App {
+ show = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, OnDestroyDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(destroyed).toBeFalsy();
+
+ fixture.componentInstance.show = false;
+ fixture.detectChanges();
+
+ expect(destroyed).toBeTruthy();
+ });
+});
diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts
index 0c3c35edca..b0883302c2 100644
--- a/packages/core/test/render3/lifecycle_spec.ts
+++ b/packages/core/test/render3/lifecycle_spec.ts
@@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ComponentFactoryResolver, OnDestroy, SimpleChange, SimpleChanges, ViewContainerRef} from '../../src/core';
-import {AttributeMarker, ComponentTemplate, LifecycleHooksFeature, injectComponentFactoryResolver, ΔNgOnChangesFeature, ΔdefineComponent, ΔdefineDirective} from '../../src/render3/index';
-import {markDirty, Δbind, Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, ΔdirectiveInject, Δelement, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δlistener, Δprojection, ΔprojectionDef, Δselect, Δtemplate, Δtext} from '../../src/render3/instructions/all';
+import {OnDestroy} from '../../src/core';
+import {AttributeMarker, ComponentTemplate, ΔNgOnChangesFeature, ΔdefineComponent, ΔdefineDirective} from '../../src/render3/index';
+import {Δbind, Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, Δelement, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δprojection, ΔprojectionDef, Δselect, Δtemplate, Δtext} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {NgIf} from './common_with_def';
-import {ComponentFixture, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame} from './render_util';
+import {ComponentFixture, createComponent} from './render_util';
describe('lifecycles', () => {
@@ -32,7 +32,7 @@ describe('lifecycles', () => {
beforeEach(() => { events = []; });
- let Comp = createOnInitComponent('comp', (rf: RenderFlags, ctx: any) => {
+ let Comp = createOnInitComponent('comp', (rf: RenderFlags) => {
if (rf & RenderFlags.Create) {
ΔprojectionDef();
ΔelementStart(0, 'div');
@@ -41,7 +41,7 @@ describe('lifecycles', () => {
}
}, 2);
let Parent = createOnInitComponent('parent', getParentTemplate('comp'), 1, 1, [Comp]);
- let ProjectedComp = createOnInitComponent('projected', (rf: RenderFlags, ctx: any) => {
+ let ProjectedComp = createOnInitComponent('projected', (rf: RenderFlags) => {
if (rf & RenderFlags.Create) {
Δtext(0, 'content');
}
@@ -115,277 +115,4 @@ describe('lifecycles', () => {
expect(events).toEqual(['comp', 'comp']);
});
});
-
- describe('hook order', () => {
- let events: string[];
-
- beforeEach(() => { events = []; });
-
- function createAllHooksComponent(
- name: string, template: ComponentTemplate, consts: number = 0, vars: number = 0,
- directives: any[] = []) {
- return class Component {
- val: string = '';
-
- ngOnChanges() { events.push(`changes ${name}${this.val}`); }
-
- ngOnInit() { events.push(`init ${name}${this.val}`); }
- ngDoCheck() { events.push(`check ${name}${this.val}`); }
-
- ngAfterContentInit() { events.push(`contentInit ${name}${this.val}`); }
- ngAfterContentChecked() { events.push(`contentCheck ${name}${this.val}`); }
-
- ngAfterViewInit() { events.push(`viewInit ${name}${this.val}`); }
- ngAfterViewChecked() { events.push(`viewCheck ${name}${this.val}`); }
-
- static ngComponentDef = ΔdefineComponent({
- type: Component,
- selectors: [[name]],
- factory: () => new Component(),
- consts: consts,
- vars: vars,
- inputs: {val: 'val'}, template,
- directives: directives,
- features: [ΔNgOnChangesFeature()],
- });
- };
- }
-
- it('should call all hooks in correct order', () => {
- const Comp = createAllHooksComponent('comp', (rf: RenderFlags, ctx: any) => {});
-
- /**
- *
- *
- */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'comp');
- Δelement(1, 'comp');
- }
- // This template function is a little weird in that the `elementProperty` calls
- // below are directly setting values `1` and `2`, where normally there would be
- // a call to `bind()` that would do the work of seeing if something changed.
- // This means when `fixture.update()` is called below, ngOnChanges should fire,
- // even though the *value* itself never changed.
- if (rf & RenderFlags.Update) {
- ΔelementProperty(0, 'val', 1);
- Δselect(1);
- ΔelementProperty(1, 'val', 2);
- }
- }, 2, 0, [Comp]);
-
- const fixture = new ComponentFixture(App);
- expect(events).toEqual([
- 'changes comp1', 'init comp1', 'check comp1', 'changes comp2', 'init comp2', 'check comp2',
- 'contentInit comp1', 'contentCheck comp1', 'contentInit comp2', 'contentCheck comp2',
- 'viewInit comp1', 'viewCheck comp1', 'viewInit comp2', 'viewCheck comp2'
- ]);
-
- events = [];
- fixture.update(); // Changes are made due to lack of `bind()` call in template fn.
- expect(events).toEqual([
- 'changes comp1', 'check comp1', 'changes comp2', 'check comp2', 'contentCheck comp1',
- 'contentCheck comp2', 'viewCheck comp1', 'viewCheck comp2'
- ]);
- });
-
- it('should call all hooks in correct order with children', () => {
- const Comp = createAllHooksComponent('comp', (rf: RenderFlags, ctx: any) => {});
-
- /** */
- const Parent = createAllHooksComponent('parent', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'comp');
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(0, 'val', Δbind(ctx.val));
- }
- }, 1, 1, [Comp]);
-
- /**
- *
- *
- */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'parent');
- Δelement(1, 'parent');
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(0, 'val', 1);
- Δselect(1);
- ΔelementProperty(1, 'val', 2);
- }
- }, 2, 0, [Parent]);
-
- const fixture = new ComponentFixture(App);
- expect(events).toEqual([
- 'changes parent1', 'init parent1', 'check parent1',
- 'changes parent2', 'init parent2', 'check parent2',
- 'contentInit parent1', 'contentCheck parent1', 'contentInit parent2',
- 'contentCheck parent2', 'changes comp1', 'init comp1',
- 'check comp1', 'contentInit comp1', 'contentCheck comp1',
- 'viewInit comp1', 'viewCheck comp1', 'changes comp2',
- 'init comp2', 'check comp2', 'contentInit comp2',
- 'contentCheck comp2', 'viewInit comp2', 'viewCheck comp2',
- 'viewInit parent1', 'viewCheck parent1', 'viewInit parent2',
- 'viewCheck parent2'
- ]);
-
- events = [];
- fixture.update();
- expect(events).toEqual([
- 'changes parent1', 'check parent1', 'changes parent2', 'check parent2',
- 'contentCheck parent1', 'contentCheck parent2', 'check comp1', 'contentCheck comp1',
- 'viewCheck comp1', 'check comp2', 'contentCheck comp2', 'viewCheck comp2',
- 'viewCheck parent1', 'viewCheck parent2'
- ]);
-
- });
-
- // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-ng
- it('should call all hooks in correct order with view and content', () => {
- const Content = createAllHooksComponent('content', (rf: RenderFlags, ctx: any) => {});
-
- const View = createAllHooksComponent('view', (rf: RenderFlags, ctx: any) => {});
-
- /** */
- const Parent = createAllHooksComponent('parent', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ΔprojectionDef();
- Δprojection(0);
- Δelement(1, 'view');
- }
- if (rf & RenderFlags.Update) {
- Δselect(1);
- ΔelementProperty(1, 'val', Δbind(ctx.val));
- }
- }, 2, 1, [View]);
-
- /**
- *
- *
- *
- *
- *
- *
- */
- const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ΔelementStart(0, 'parent');
- { Δelement(1, 'content'); }
- ΔelementEnd();
- ΔelementStart(2, 'parent');
- { Δelement(3, 'content'); }
- ΔelementEnd();
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(0, 'val', Δbind(1));
- Δselect(1);
- ΔelementProperty(1, 'val', Δbind(1));
- Δselect(2);
- ΔelementProperty(2, 'val', Δbind(2));
- Δselect(3);
- ΔelementProperty(3, 'val', Δbind(2));
- }
- }, 4, 4, [Parent, Content]);
-
- const fixture = new ComponentFixture(App);
- expect(events).toEqual([
- 'changes parent1', 'init parent1',
- 'check parent1', 'changes content1',
- 'init content1', 'check content1',
- 'changes parent2', 'init parent2',
- 'check parent2', 'changes content2',
- 'init content2', 'check content2',
- 'contentInit content1', 'contentCheck content1',
- 'contentInit parent1', 'contentCheck parent1',
- 'contentInit content2', 'contentCheck content2',
- 'contentInit parent2', 'contentCheck parent2',
- 'changes view1', 'init view1',
- 'check view1', 'contentInit view1',
- 'contentCheck view1', 'viewInit view1',
- 'viewCheck view1', 'changes view2',
- 'init view2', 'check view2',
- 'contentInit view2', 'contentCheck view2',
- 'viewInit view2', 'viewCheck view2',
- 'viewInit content1', 'viewCheck content1',
- 'viewInit parent1', 'viewCheck parent1',
- 'viewInit content2', 'viewCheck content2',
- 'viewInit parent2', 'viewCheck parent2'
- ]);
-
- events = [];
- fixture.update();
- expect(events).toEqual([
- 'check parent1', 'check content1', 'check parent2', 'check content2',
- 'contentCheck content1', 'contentCheck parent1', 'contentCheck content2',
- 'contentCheck parent2', 'check view1', 'contentCheck view1', 'viewCheck view1',
- 'check view2', 'contentCheck view2', 'viewCheck view2', 'viewCheck content1',
- 'viewCheck parent1', 'viewCheck content2', 'viewCheck parent2'
- ]);
-
- });
-
- });
-
- describe('non-regression', () => {
-
- it('should call lifecycle hooks for directives active on ', () => {
- let destroyed = false;
-
- class OnDestroyDirective implements OnDestroy {
- ngOnDestroy() { destroyed = true; }
-
- static ngDirectiveDef = ΔdefineDirective({
- type: OnDestroyDirective,
- selectors: [['', 'onDestroyDirective', '']],
- factory: () => new OnDestroyDirective()
- });
- }
-
-
- function conditionTpl(rf: RenderFlags, ctx: Cmpt) {
- if (rf & RenderFlags.Create) {
- Δtemplate(0, null, 0, 1, 'ng-template', [AttributeMarker.Bindings, 'onDestroyDirective']);
- }
- }
-
- /**
- *
- *
- *
- */
- function cmptTpl(rf: RenderFlags, cmpt: Cmpt) {
- if (rf & RenderFlags.Create) {
- Δtemplate(0, conditionTpl, 1, 1, 'ng-template', [AttributeMarker.Bindings, 'ngIf']);
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(0, 'ngIf', Δbind(cmpt.showing));
- }
- }
-
- class Cmpt {
- showing = true;
- static ngComponentDef = ΔdefineComponent({
- type: Cmpt,
- factory: () => new Cmpt(),
- selectors: [['cmpt']],
- consts: 1,
- vars: 1,
- template: cmptTpl,
- directives: [NgIf, OnDestroyDirective]
- });
- }
-
- const fixture = new ComponentFixture(Cmpt);
- expect(destroyed).toBeFalsy();
-
- fixture.component.showing = false;
- fixture.update();
- expect(destroyed).toBeTruthy();
- });
- });
-
});