diff --git a/packages/core/test/acceptance/lifecycle_spec.ts b/packages/core/test/acceptance/lifecycle_spec.ts index 7c94c95435..a99d80120a 100644 --- a/packages/core/test/acceptance/lifecycle_spec.ts +++ b/packages/core/test/acceptance/lifecycle_spec.ts @@ -1209,3 +1209,681 @@ describe('afterContentChecked', () => { ]); }); }); + +describe('afterViewInit', () => { + it('should be called on creation and not in update mode', () => { + let afterViewInitCalls = 0; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + ngAfterViewInit() { afterViewInitCalls++; } + } + + @Component({template: ``}) + class App { + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // two updates + fixture.detectChanges(); + fixture.detectChanges(); + + expect(afterViewInitCalls).toBe(1); + + }); + + it('should be called on root component in creation mode', () => { + let afterViewInitCalls = 0; + + @Component({template: `

test

`}) + class App { + ngAfterViewInit() { afterViewInitCalls++; } + } + + TestBed.configureTestingModule({ + declarations: [App], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + // two updates + fixture.detectChanges(); + fixture.detectChanges(); + + expect(afterViewInitCalls).toBe(1); + }); + + it('should be called every time a view is initialized with ngIf', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + ngAfterViewInit() { events.push('comp'); } + } + + @Component({ + template: ``, + }) + class App { + show = true; + + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual(['comp', 'app']); + + fixture.componentInstance.show = false; + fixture.detectChanges(); + + expect(events).toEqual(['comp', 'app']); + + fixture.componentInstance.show = true; + fixture.detectChanges(); + + expect(events).toEqual(['comp', 'app', 'comp']); + }); + + it('should be called in children before parents', () => { + const events: string[] = []; + + @Component({ + selector: 'parent', + template: ``, + }) + class Parent { + @Input() + name = ''; + + ngAfterViewInit() { events.push('parent ' + this.name); } + } + + @Component({ + selector: 'child', + template: `

test

`, + }) + class Child { + @Input() + name = ''; + + ngAfterViewInit() { events.push('child of parent ' + this.name); } + } + + @Component({ + template: ` + + + ` + }) + class App { + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Parent, Child], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'child of parent 1', + 'child of parent 2', + 'parent 1', + 'parent 2', + 'app', + ]); + }); + + it('should be called in projected components before their hosts', () => { + const events: string[] = []; + + @Component({ + selector: 'projected', + template: `

test

`, + }) + class Projected { + @Input() + name = ''; + + ngAfterViewInit() { events.push('projected ' + this.name); } + } + + @Component({ + selector: 'comp', + template: ``, + }) + class Comp { + @Input() + name = ''; + + ngAfterViewInit() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` + + + ` + }) + class App { + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp, Projected], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + + expect(events).toEqual([ + 'projected 1', + 'comp 1', + 'projected 2', + 'comp 2', + 'app', + ]); + }); + + it('should call afterViewInit in content children and host before next host', () => { + const events: string[] = []; + + @Component({ + selector: 'projected-child', + template: `

test

`, + }) + class ProjectedChild { + @Input() + name = ''; + + ngAfterViewInit() { events.push('child of projected ' + this.name); } + } + + @Component({ + selector: 'projected', + template: ``, + }) + class Projected { + @Input() + name = ''; + + ngAfterViewInit() { events.push('projected ' + this.name); } + } + + @Component({ + selector: 'comp', + template: `
`, + }) + class Comp { + @Input() + name = ''; + + ngAfterViewInit() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` + + + ` + }) + class App { + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp, Projected, ProjectedChild], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'child of projected 1', + 'child of projected 2', + 'projected 1', + 'comp 1', + 'projected 2', + 'comp 2', + 'app', + ]); + }); + + it('should be called in correct order with ngFor', () => { + const events: string[] = []; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngAfterViewInit() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` + + + + ` + }) + class App { + numbers = [0, 1, 2, 3]; + + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 0', + 'comp 1', + 'comp 2', + 'comp 3', + 'comp 4', + 'comp 5', + 'app', + ]); + }); + + it('should be called in correct order with for loops with children', () => { + const events: string[] = []; + + @Component({ + selector: 'child', + template: `

test

`, + }) + class Child { + @Input() + name = ''; + + ngAfterViewInit() { events.push('child of parent ' + this.name); } + } + @Component({ + selector: 'parent', + template: ``, + }) + class Parent { + @Input() + name = ''; + + ngAfterViewInit() { events.push('parent ' + this.name); } + } + + @Component({ + template: ` + + + + ` + }) + class App { + numbers = [0, 1, 2, 3]; + + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Parent, Child], + imports: [CommonModule], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'child of parent 0', + 'parent 0', + 'child of parent 1', + 'parent 1', + 'child of parent 2', + 'parent 2', + 'child of parent 3', + 'parent 3', + 'child of parent 4', + 'child of parent 5', + 'parent 4', + 'parent 5', + 'app', + ]); + }); + + it('should be called on directives after component', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + @Input('dir') + name = ''; + + ngAfterViewInit() { events.push('dir ' + this.name); } + } + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngAfterViewInit() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` + + + ` + }) + class App { + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 1', + 'dir 1', + 'comp 2', + 'dir 2', + 'app', + ]); + }); + + it('should be called on directives on an element', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + @Input('dir') + name = ''; + + ngAfterViewInit() { events.push('dir ' + this.name); } + } + + @Component({ + template: ` +
+
+ ` + }) + class App { + ngAfterViewInit() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'dir 1', + 'dir 2', + 'app', + ]); + }); +}); + +describe('ngAfterViewChecked', () => { + it('should call ngAfterViewChecked every update', () => { + let afterViewCheckedCalls = 0; + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + ngAfterViewChecked() { afterViewCheckedCalls++; } + } + + @Component({template: ``}) + class App { + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(1); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(2); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(3); + }); + + it('should be called on root component', () => { + let afterViewCheckedCalls = 0; + + @Component({template: `

test

`}) + class App { + ngAfterViewChecked() { afterViewCheckedCalls++; } + } + + TestBed.configureTestingModule({ + declarations: [App], + }); + const fixture = TestBed.createComponent(App); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(1); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(2); + + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(3); + }); + + it('should call ngAfterViewChecked with bindings', () => { + let afterViewCheckedCalls = 0; + + @Component({ + selector: 'comp', + template: `

{{value}}

`, + }) + class Comp { + @Input() + value = ''; + ngAfterViewChecked() { afterViewCheckedCalls++; } + } + + @Component({template: ``}) + class App { + value = 1; + } + + TestBed.configureTestingModule({ + declarations: [App, Comp], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(1); + + fixture.componentInstance.value = 1337; + fixture.detectChanges(); + expect(afterViewCheckedCalls).toBe(2); + }); + + it('should be called in correct order with for loops with children', () => { + const events: string[] = []; + + @Component({ + selector: 'child', + template: `

test

`, + }) + class Child { + @Input() + name = ''; + + ngAfterViewChecked() { events.push('child of parent ' + this.name); } + } + + @Component({ + selector: 'parent', + template: ``, + }) + class Parent { + @Input() + name = ''; + + ngAfterViewChecked() { events.push('parent ' + this.name); } + } + + @Component({ + template: ` + + + + ` + }) + class App { + numbers = [0, 1, 2, 3]; + + ngAfterViewChecked() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Parent, Child], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'child of parent 0', + 'parent 0', + 'child of parent 1', + 'parent 1', + 'child of parent 2', + 'parent 2', + 'child of parent 3', + 'parent 3', + 'child of parent 4', + 'child of parent 5', + 'parent 4', + 'parent 5', + 'app', + ]); + }); + + it('should be called on directives after component', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + @Input('dir') + name = ''; + + ngAfterViewChecked() { events.push('dir ' + this.name); } + } + + @Component({ + selector: 'comp', + template: `

test

`, + }) + class Comp { + @Input() + name = ''; + + ngAfterViewChecked() { events.push('comp ' + this.name); } + } + + @Component({ + template: ` + + + ` + }) + class App { + ngAfterViewChecked() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Comp, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'comp 1', + 'dir 1', + 'comp 2', + 'dir 2', + 'app', + ]); + }); + + it('should be called on directives on an element', () => { + const events: string[] = []; + + @Directive({ + selector: '[dir]', + }) + class Dir { + @Input('dir') + name = ''; + + ngAfterViewChecked() { events.push('dir ' + this.name); } + } + + @Component({ + template: ` +
+
+ ` + }) + class App { + ngAfterViewChecked() { events.push('app'); } + } + + TestBed.configureTestingModule({ + declarations: [App, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + expect(events).toEqual([ + 'dir 1', + 'dir 2', + 'app', + ]); + }); + +}); diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index 7cd4f67d2b..0989f3db3a 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -116,858 +116,6 @@ describe('lifecycles', () => { }); }); - describe('afterContentInit', () => { - let events: string[]; - let allEvents: string[]; - - beforeEach(() => { - events = []; - allEvents = []; - }); - - let Comp = createAfterContentInitComp('comp', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔprojectionDef(); - Δprojection(0); - } - }, 1); - - let Parent = createAfterContentInitComp('parent', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔprojectionDef(); - ΔelementStart(0, 'comp'); - { Δprojection(1); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind(ctx.val)); - } - }, 2, 1, [Comp]); - - let ProjectedComp = createAfterContentInitComp('projected', (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - ΔprojectionDef(); - Δprojection(0); - } - }, 1); - - function createAfterContentInitComp( - name: string, template: ComponentTemplate, consts: number = 0, vars: number = 0, - directives: any[] = []) { - return class Component { - val: string = ''; - ngAfterContentInit() { - events.push(`${name}${this.val}`); - allEvents.push(`${name}${this.val} init`); - } - ngAfterContentChecked() { allEvents.push(`${name}${this.val} check`); } - - static ngComponentDef = ΔdefineComponent({ - type: Component, - selectors: [[name]], - factory: () => new Component(), - consts: consts, - vars: vars, - inputs: {val: 'val'}, - template: template, - directives: directives - }); - }; - } - - class Directive { - ngAfterContentInit() { events.push('init'); } - ngAfterContentChecked() { events.push('check'); } - - static ngDirectiveDef = ΔdefineDirective( - {type: Directive, selectors: [['', 'dir', '']], factory: () => new Directive()}); - } - - function ForLoopWithChildrenTemplate(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { Δtext(1, 'content'); } - ΔelementEnd(); - Δcontainer(2); - ΔelementStart(3, 'parent'); - { Δtext(4, 'content'); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(3); - ΔelementProperty(3, 'val', 4); - ΔcontainerRefreshStart(2); - { - for (let i = 2; i < 4; i++) { - let rf1 = ΔembeddedViewStart(0, 2, 0); - if (rf1 & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', i); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - } - - const directives = [Comp, Parent, ProjectedComp, Directive]; - - it('should be called only in creation mode', () => { - /** content */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - }, 2, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp']); - - fixture.update(); - expect(events).toEqual(['comp']); - }); - - it('should be called on root component in creation mode', () => { - const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]}); - expect(events).toEqual(['comp']); - - markDirty(comp); - requestAnimationFrame.flush(); - expect(events).toEqual(['comp']); - }); - - it('should be called on every init (if blocks)', () => { - /** - * % if (!skip) { - * content - * % } - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (!ctx.skip) { - let rf1 = ΔembeddedViewStart(0, 2, 0); - if (rf1 & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp']); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp']); - - fixture.component.skip = false; - fixture.update(); - expect(events).toEqual(['comp', 'comp']); - }); - - it('should be called in parents before children', () => { - /** - * content - * - * parent template: - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - }, 2, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['parent', 'comp']); - }); - - it('should be called breadth-first in entire parent subtree before any children', () => { - /** - * content - * content - * - * parent template: - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { Δtext(1, 'content'); } - ΔelementEnd(); - ΔelementStart(2, 'parent'); - { Δtext(3, 'content'); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 2); - } - }, 4, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['parent1', 'parent2', 'comp1', 'comp2']); - }); - - it('should be called in projected components before their hosts', () => { - /** - * - * content - * - * - * parent template: - * - * - * projected comp: - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { - ΔelementStart(1, 'projected'); - { Δtext(2, 'content'); } - ΔelementEnd(); - } - ΔelementEnd(); - } - }, 3, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['projected', 'parent', 'comp']); - }); - - it('should be called in projected components and hosts before children', () => { - /** - * - * content - * - * * - * content - * - * - * parent template: - * - * - * projected comp: - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'parent'); - { - ΔelementStart(1, 'projected'); - { Δtext(2, 'content'); } - ΔelementEnd(); - } - ΔelementEnd(); - ΔelementStart(3, 'parent'); - { - ΔelementStart(4, 'projected'); - { Δtext(5, 'content'); } - ΔelementEnd(); - } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(1); - ΔelementProperty(1, 'val', 1); - Δselect(3); - ΔelementProperty(3, 'val', 2); - Δselect(4); - ΔelementProperty(4, 'val', 2); - } - }, 6, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['projected1', 'parent1', 'projected2', 'parent2', 'comp1', 'comp2']); - }); - - it('should be called in correct order in a for loop', () => { - /** - * content - * % for(let i = 2; i < 4; i++) { - * content - * % } - * content - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δtext(1, 'content'); } - ΔelementEnd(); - Δcontainer(2); - ΔelementStart(3, 'comp'); - { Δtext(4, 'content'); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(3); - ΔelementProperty(3, 'val', 4); - ΔcontainerRefreshStart(2); - { - for (let i = 2; i < 4; i++) { - let rf1 = ΔembeddedViewStart(0, 2, 0); - if (rf1 & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', i); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 5, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']); - }); - - it('should be called in correct order in a for loop with children', () => { - /** - * content - * % for(let i = 2; i < 4; i++) { - * content - * % } - * content - */ - - renderToHtml(ForLoopWithChildrenTemplate, {}, 5, 0, directives); - expect(events).toEqual( - ['parent2', 'comp2', 'parent3', 'comp3', 'parent1', 'parent4', 'comp1', 'comp4']); - }); - - describe('ngAfterContentChecked', () => { - - it('should be called every change detection run after afterContentInit', () => { - /** content */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δtext(1, 'content'); } - ΔelementEnd(); - } - }, 2, 0, directives); - - const fixture = new ComponentFixture(App); - expect(allEvents).toEqual(['comp init', 'comp check']); - - fixture.update(); - expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); - }); - - it('should be called on root component', () => { - const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]}); - expect(allEvents).toEqual(['comp init', 'comp check']); - - markDirty(comp); - requestAnimationFrame.flush(); - expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); - }); - - }); - - describe('directives', () => { - it('should be called on directives after component', () => { - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp', ['dir', '']); - } - }, 1, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp', 'init', 'check']); - }); - - it('should be called on directives on an element', () => { - /**
*/ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'div', ['dir', '']); - } - }, 1, 0, directives); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['init', 'check']); - }); - }); - }); - - describe('afterViewInit', () => { - let events: string[]; - let allEvents: string[]; - - beforeEach(() => { - events = []; - allEvents = []; - }); - - let Comp = createAfterViewInitComponent('comp', (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - ΔprojectionDef(); - ΔelementStart(0, 'div'); - { Δprojection(1); } - ΔelementEnd(); - } - }, 2); - let Parent = createAfterViewInitComponent('parent', getParentTemplate('comp'), 1, 1, [Comp]); - - let ProjectedComp = createAfterViewInitComponent('projected', (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - Δtext(0, 'content'); - } - }, 1); - - function createAfterViewInitComponent( - name: string, template: ComponentTemplate, consts: number, vars: number = 0, - directives: any[] = []) { - return class Component { - val: string = ''; - ngAfterViewInit() { - if (!this.val) this.val = ''; - events.push(`${name}${this.val}`); - allEvents.push(`${name}${this.val} init`); - } - ngAfterViewChecked() { allEvents.push(`${name}${this.val} check`); } - - static ngComponentDef = ΔdefineComponent({ - type: Component, - selectors: [[name]], - consts: consts, - vars: vars, - factory: () => new Component(), - inputs: {val: 'val'}, - template: template, - directives: directives - }); - }; - } - - class Directive { - ngAfterViewInit() { events.push('init'); } - ngAfterViewChecked() { events.push('check'); } - - static ngDirectiveDef = ΔdefineDirective( - {type: Directive, selectors: [['', 'dir', '']], factory: () => new Directive()}); - } - - const defs = [Comp, Parent, ProjectedComp, Directive]; - - it('should be called on init and not in update mode', () => { - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp'); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp']); - - fixture.update(); - expect(events).toEqual(['comp']); - }); - - it('should be called on root component in creation mode', () => { - const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]}); - expect(events).toEqual(['comp']); - - markDirty(comp); - requestAnimationFrame.flush(); - expect(events).toEqual(['comp']); - }); - - it('should be called every time a view is initialized (if block)', () => { - /* - * % if (!skip) { - * - * % } - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δcontainer(0); - } - if (rf & RenderFlags.Update) { - ΔcontainerRefreshStart(0); - { - if (!ctx.skip) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp'); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp']); - - fixture.component.skip = true; - fixture.update(); - expect(events).toEqual(['comp']); - - fixture.component.skip = false; - fixture.update(); - expect(events).toEqual(['comp', 'comp']); - - }); - - it('should be called in children before parents', () => { - /** - * - * - * parent temp: - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'parent'); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp', 'parent']); - }); - - it('should be called for entire subtree before being called in any parent view comps', () => { - /** - * - * - * - * parent temp: - */ - 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, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp1', 'comp2', 'parent1', 'parent2']); - - }); - - it('should be called in projected components before their hosts', () => { - /** - * - * - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δelement(1, 'projected'); } - ΔelementEnd(); - } - }, 2, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['projected', 'comp']); - }); - - it('should call afterViewInit in content children and host before next host', () => { - /** - * - * - * - * - * - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δelement(1, 'projected'); } - ΔelementEnd(); - ΔelementStart(2, 'comp'); - { Δelement(3, 'projected'); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(1); - ΔelementProperty(1, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 2); - Δselect(3); - ΔelementProperty(3, 'val', 2); - } - }, 4, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2']); - }); - - it('should call afterViewInit in content children and hosts before parents', () => { - /* - * - * - * - */ - const ParentComp = createAfterViewInitComponent('parent', (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - ΔelementStart(0, 'comp'); - { Δelement(1, 'projected'); } - ΔelementEnd(); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind(ctx.val)); - Δselect(1); - ΔelementProperty(1, 'val', Δbind(ctx.val)); - } - }, 2, 2, [Comp, ProjectedComp]); - - /** - * - * - */ - 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, [ParentComp]); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['projected1', 'comp1', 'projected2', 'comp2', 'parent1', 'parent2']); - }); - - it('should be called in correct order with for loops', () => { - /** - * - * % for (let i = 0; i < 4; i++) { - * - * % } - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp'); - Δcontainer(1); - Δelement(2, 'comp'); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 4); - ΔcontainerRefreshStart(1); - { - for (let i = 2; i < 4; i++) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'comp'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', i); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 3, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']); - - }); - - it('should be called in correct order with for loops with children', () => { - /** - * - * % for(let i = 0; i < 4; i++) { - * - * % } - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'parent'); - Δcontainer(1); - Δelement(2, 'parent'); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 4); - ΔcontainerRefreshStart(1); - { - for (let i = 2; i < 4; i++) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'parent'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', i); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 3, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual( - ['comp2', 'parent2', 'comp3', 'parent3', 'comp1', 'comp4', 'parent1', 'parent4']); - - }); - - describe('ngAfterViewChecked', () => { - - it('should call ngAfterViewChecked every update', () => { - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp'); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(allEvents).toEqual(['comp init', 'comp check']); - - fixture.update(); - expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); - }); - - it('should be called on root component', () => { - const comp = renderComponent(Comp, {hostFeatures: [LifecycleHooksFeature]}); - expect(allEvents).toEqual(['comp init', 'comp check']); - - markDirty(comp); - requestAnimationFrame.flush(); - expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']); - }); - - it('should call ngAfterViewChecked with bindings', () => { - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp'); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', Δbind(ctx.myVal)); - } - }, 1, 1, defs); - - const fixture = new ComponentFixture(App); - expect(allEvents).toEqual(['comp init', 'comp check']); - - fixture.component.myVal = 2; - fixture.update(); - expect(allEvents).toEqual(['comp init', 'comp check', 'comp2 check']); - }); - - it('should be called in correct order with for loops with children', () => { - /** - * - * % for(let i = 0; i < 4; i++) { - * - * % } - * - */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'parent'); - Δcontainer(1); - Δelement(2, 'parent'); - } - if (rf & RenderFlags.Update) { - ΔelementProperty(0, 'val', 1); - Δselect(2); - ΔelementProperty(2, 'val', 4); - ΔcontainerRefreshStart(1); - { - for (let i = 2; i < 4; i++) { - let rf1 = ΔembeddedViewStart(0, 1, 0); - if (rf1 & RenderFlags.Create) { - Δelement(0, 'parent'); - } - if (rf1 & RenderFlags.Update) { - ΔelementProperty(0, 'val', i); - } - ΔembeddedViewEnd(); - } - } - ΔcontainerRefreshEnd(); - } - }, 3, 0, defs); - - const fixture = new ComponentFixture(App); - expect(allEvents).toEqual([ - 'comp2 init', 'comp2 check', 'parent2 init', 'parent2 check', 'comp3 init', 'comp3 check', - 'parent3 init', 'parent3 check', 'comp1 init', 'comp1 check', 'comp4 init', 'comp4 check', - 'parent1 init', 'parent1 check', 'parent4 init', 'parent4 check' - ]); - - }); - - }); - - describe('directives', () => { - it('should be called on directives after component', () => { - /** */ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'comp', ['dir', '']); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['comp', 'init', 'check']); - }); - - it('should be called on directives on an element', () => { - /**
*/ - const App = createComponent('app', function(rf: RenderFlags, ctx: any) { - if (rf & RenderFlags.Create) { - Δelement(0, 'div', ['dir', '']); - } - }, 1, 0, defs); - - const fixture = new ComponentFixture(App); - expect(events).toEqual(['init', 'check']); - }); - }); - }); - describe('onDestroy', () => { let events: string[];