feat(core): add ngAfterViewInit and ngAfterViewChecked support to render3 (#21266)

PR Close #21266
This commit is contained in:
Kara Erickson
2017-12-22 16:41:34 -08:00
parent 229b76cfde
commit 3db91ffd96
3 changed files with 439 additions and 49 deletions

View File

@ -12,22 +12,26 @@ import {containerEl, renderToHtml} from './render_util';
describe('lifecycles', () => {
function getParentTemplate(type: any) {
return (ctx: any, cm: boolean) => {
if (cm) {
E(0, type.ngComponentDef);
{ D(1, type.ngComponentDef.n(), type.ngComponentDef); }
e();
}
p(0, 'val', b(ctx.val));
type.ngComponentDef.h(1, 0);
type.ngComponentDef.r(1, 0);
};
}
describe('onInit', () => {
let events: string[];
beforeEach(() => { events = []; });
let Comp = createOnInitComponent('comp', (ctx: any, cm: boolean) => {});
let Parent = createOnInitComponent('parent', (ctx: any, cm: boolean) => {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
p(0, 'val', b(ctx.val));
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
});
let Parent = createOnInitComponent('parent', getParentTemplate(Comp));
function createOnInitComponent(name: string, template: ComponentTemplate<any>) {
return class Component {
@ -104,8 +108,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', 1);
Parent.ngComponentDef.h(1, 0);
p(2, 'val', 2);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.h(3, 2);
Parent.ngComponentDef.r(1, 0);
Parent.ngComponentDef.r(3, 2);
@ -175,8 +179,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', 1);
Comp.ngComponentDef.h(1, 0);
p(3, 'val', 5);
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.h(4, 3);
cR(2);
{
@ -225,8 +229,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', 1);
Parent.ngComponentDef.h(1, 0);
p(3, 'val', 5);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.h(4, 3);
cR(2);
{
@ -270,15 +274,7 @@ describe('lifecycles', () => {
});
let Comp = createDoCheckComponent('comp', (ctx: any, cm: boolean) => {});
let Parent = createDoCheckComponent('parent', (ctx: any, cm: boolean) => {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
});
let Parent = createDoCheckComponent('parent', getParentTemplate(Comp));
function createDoCheckComponent(name: string, template: ComponentTemplate<any>) {
return class Component {
@ -363,21 +359,348 @@ describe('lifecycles', () => {
});
describe('ngAfterViewInit', () => {
let events: string[];
let allEvents: string[];
beforeEach(() => {
events = [];
allEvents = [];
});
let Comp = createAfterViewInitComponent('comp', function(ctx: any, cm: boolean) {});
let Parent = createAfterViewInitComponent('parent', getParentTemplate(Comp));
function createAfterViewInitComponent(name: string, template: ComponentTemplate<any>) {
return class Component {
val: string = '';
ngAfterViewInit() {
events.push(`${name}${this.val}`);
allEvents.push(`${name}${this.val} init`);
}
ngAfterViewChecked() { allEvents.push(`${name}${this.val} check`); }
static ngComponentDef = defineComponent({
type: Component,
tag: name,
factory: () => new Component(),
refresh: (directiveIndex: number, elementIndex: number) => {
r(directiveIndex, elementIndex, template);
const comp = D(directiveIndex) as Component;
l(LifecycleHook.AFTER_VIEW_INIT, comp, comp.ngAfterViewInit);
l(LifecycleHook.AFTER_VIEW_CHECKED, comp, comp.ngAfterViewChecked);
},
inputs: {val: 'val'},
template: template
});
};
}
it('should be called on init and not in update mode', () => {
/** <comp></comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
}
renderToHtml(Template, {});
expect(events).toEqual(['comp']);
renderToHtml(Template, {});
expect(events).toEqual(['comp']);
});
it('should be called every time a view is initialized (if block)', () => {
/*
* % if (condition) {
* <comp></comp>
* % }
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
C(0);
c();
}
cR(0);
{
if (ctx.condition) {
if (V(0)) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
v();
}
}
cr();
}
renderToHtml(Template, {condition: true});
expect(events).toEqual(['comp']);
renderToHtml(Template, {condition: false});
expect(events).toEqual(['comp']);
renderToHtml(Template, {condition: true});
expect(events).toEqual(['comp', 'comp']);
});
it('should be called in children before parents', () => {
/**
* <parent></parent>
*
* parent temp: <comp></comp>
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.r(1, 0);
}
renderToHtml(Template, {});
expect(events).toEqual(['comp', 'parent']);
});
it('should be called for entire subtree before being called in any parent view comps', () => {
/**
* <parent [val]="1"></parent>
* <parent [val]="2"></parent>
*
* parent temp: <comp [val]="val"></comp>
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
E(2, Parent.ngComponentDef);
{ D(3, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
p(0, 'val', 1);
p(2, 'val', 2);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.h(3, 2);
Parent.ngComponentDef.r(1, 0);
Parent.ngComponentDef.r(3, 2);
}
renderToHtml(Template, {});
expect(events).toEqual(['comp1', 'comp2', 'parent1', 'parent2']);
});
it('should be called in correct order with for loops', () => {
/**
* <comp [val]="1"></comp>
* % for (let i = 0; i < 4; i++) {
* <comp [val]="i"></comp>
* % }
* <comp [val]="4"></comp>
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
C(2);
c();
E(3, Comp.ngComponentDef);
{ D(4, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
p(0, 'val', 1);
p(3, 'val', 4);
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.h(4, 3);
cR(2);
{
for (let i = 2; i < 4; i++) {
if (V(0)) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
p(0, 'val', i);
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
v();
}
}
cr();
Comp.ngComponentDef.r(1, 0);
Comp.ngComponentDef.r(4, 3);
}
renderToHtml(Template, {});
expect(events).toEqual(['comp2', 'comp3', 'comp1', 'comp4']);
});
it('should be called in correct order with for loops with children', () => {
/**
* <parent [val]="1"></parent>
* % for(let i = 0; i < 4; i++) {
* <parent [val]="i"></parent>
* % }
* <parent [val]="4"></parent>
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
C(2);
c();
E(3, Parent.ngComponentDef);
{ D(4, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
p(0, 'val', 1);
p(3, 'val', 4);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.h(4, 3);
cR(2);
{
for (let i = 2; i < 4; i++) {
if (V(0)) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
p(0, 'val', i);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.r(1, 0);
v();
}
}
cr();
Parent.ngComponentDef.r(1, 0);
Parent.ngComponentDef.r(4, 3);
}
renderToHtml(Template, {});
expect(events).toEqual(
['comp2', 'parent2', 'comp3', 'parent3', 'comp1', 'comp4', 'parent1', 'parent4']);
});
describe('ngAfterViewChecked', () => {
it('should call ngAfterViewChecked every update', () => {
/** <comp></comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
}
renderToHtml(Template, {});
expect(allEvents).toEqual(['comp init', 'comp check']);
renderToHtml(Template, {});
expect(allEvents).toEqual(['comp init', 'comp check', 'comp check']);
});
it('should call ngAfterViewChecked with bindings', () => {
/** <comp [val]="myVal"></comp> */
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
p(0, 'val', b(ctx.myVal));
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
}
renderToHtml(Template, {myVal: 5});
expect(allEvents).toEqual(['comp5 init', 'comp5 check']);
renderToHtml(Template, {myVal: 6});
expect(allEvents).toEqual(['comp5 init', 'comp5 check', 'comp6 check']);
});
it('should be called in correct order with for loops with children', () => {
/**
* <parent [val]="1"></parent>
* % for(let i = 0; i < 4; i++) {
* <parent [val]="i"></parent>
* % }
* <parent [val]="4"></parent>
*/
function Template(ctx: any, cm: boolean) {
if (cm) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
C(2);
c();
E(3, Parent.ngComponentDef);
{ D(4, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
p(0, 'val', 1);
p(3, 'val', 4);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.h(4, 3);
cR(2);
{
for (let i = 2; i < 4; i++) {
if (V(0)) {
E(0, Parent.ngComponentDef);
{ D(1, Parent.ngComponentDef.n(), Parent.ngComponentDef); }
e();
}
p(0, 'val', i);
Parent.ngComponentDef.h(1, 0);
Parent.ngComponentDef.r(1, 0);
v();
}
}
cr();
Parent.ngComponentDef.r(1, 0);
Parent.ngComponentDef.r(4, 3);
}
renderToHtml(Template, {});
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('onDestroy', () => {
let events: string[];
beforeEach(() => { events = []; });
let Comp = createOnDestroyComponent('comp', function(ctx: any, cm: boolean) {});
let Parent = createOnDestroyComponent('parent', function(ctx: any, cm: boolean) {
if (cm) {
E(0, Comp.ngComponentDef);
{ D(1, Comp.ngComponentDef.n(), Comp.ngComponentDef); }
e();
}
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.r(1, 0);
});
let Parent = createOnDestroyComponent('parent', getParentTemplate(Comp));
function createOnDestroyComponent(name: string, template: ComponentTemplate<any>) {
return class Component {
@ -456,8 +779,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', b('1'));
Comp.ngComponentDef.h(1, 0);
p(2, 'val', b('2'));
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.h(3, 2);
Comp.ngComponentDef.r(1, 0);
Comp.ngComponentDef.r(3, 2);
@ -584,8 +907,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', b('1'));
Comp.ngComponentDef.h(1, 0);
p(3, 'val', b('3'));
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.h(4, 3);
cR(2);
{
@ -665,8 +988,8 @@ describe('lifecycles', () => {
e();
}
p(0, 'val', b('1'));
Comp.ngComponentDef.h(1, 0);
p(3, 'val', b('5'));
Comp.ngComponentDef.h(1, 0);
Comp.ngComponentDef.h(4, 3);
cR(2);
{