Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ivy): generate lifecycle pattern #21865

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions packages/compiler/src/render3/r3_view_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {BindingForm, BuiltinConverter, EventHandlerVars, LocalResolver, convertA
import {ConstantPool, DefinitionKind} from '../constant_pool';
import {AST} from '../expression_parser/ast';
import {Identifiers} from '../identifiers';
import {LifecycleHooks} from '../lifecycle_reflector';
import * as o from '../output/output_ast';
import {ParseSourceSpan} from '../parse_util';
import {CssSelector} from '../selector';
Expand All @@ -20,8 +21,6 @@ import {OutputContext, error} from '../util';

import {Identifiers as R3} from './r3_identifiers';



/** Name of the context parameter passed into a template function */
const CONTEXT_NAME = 'ctx';

Expand Down Expand Up @@ -49,6 +48,12 @@ export function compileDirective(
const templateFactory = createFactory(directive.type, outputCtx, reflector);
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});

// e.g 'inputs: {a: 'a'}`
if (Object.getOwnPropertyNames(directive.inputs).length > 0) {
definitionMapValues.push(
{key: 'inputs', quoted: false, value: mapToExpression(directive.inputs)});
}

const className = identifierName(directive.type) !;
className || error(`Cannot resolver the name of ${directive.type}`);

Expand Down Expand Up @@ -114,6 +119,21 @@ export function compileComponent(
.buildTemplateFunction(template, []);
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});

// e.g `inputs: {a: 'a'}`
if (Object.getOwnPropertyNames(component.inputs).length > 0) {
definitionMapValues.push(
{key: 'inputs', quoted: false, value: mapToExpression(component.inputs)});
}

// e.g. `features: [NgOnChangesFeature(MyComponent)]`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the type will be present on DirectiveDef again, so it will just be features: [NgOnChangesFeature] in the near future.

const features: o.Expression[] = [];
if (component.type.lifecycleHooks.some(lifecycle => lifecycle == LifecycleHooks.OnChanges)) {
features.push(o.importExpr(R3.NgOnChangesFeature, null, null).callFn([outputCtx.importExpr(
component.type.reference)]));
}
if (features.length) {
definitionMapValues.push({key: 'features', quoted: false, value: o.literalArr(features)});
}

const className = identifierName(component.type) !;
className || error(`Cannot resolver the name of ${component.type}`);
Expand Down Expand Up @@ -282,6 +302,10 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {

templateVisitAll(this, asts);

const creationMode = this._creationMode.length > 0 ?
[o.ifStmt(o.variable(CREATION_MODE_FLAG), this._creationMode)] :
[];

return o.fn(
[
new o.FnParam(this.contextParameter, null), new o.FnParam(CREATION_MODE_FLAG, o.BOOL_TYPE)
Expand All @@ -291,7 +315,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
...this._prefix,

// Creating mode (i.e. if (cm) { ... })
o.ifStmt(o.variable(CREATION_MODE_FLAG), this._creationMode),
...creationMode,

// Binding mode (i.e. ɵp(...))
...this._bindingMode,
Expand Down Expand Up @@ -464,6 +488,12 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
o.importExpr(R3.bind).callFn([convertedBinding.currValExpr]));
}

// e.g. MyDirective.ngDirectiveDef.h(0, 0);
this._hostMode.push(
this.definitionOf(directiveType, kind)
.callMethod(R3.HOST_BINDING_METHOD, [o.literal(directiveIndex), o.literal(nodeIndex)])
.toStmt());

// e.g. r(0, 0);
this.instruction(
this._refreshMode, directive.sourceSpan, R3.refreshComponent, o.literal(directiveIndex),
Expand Down Expand Up @@ -695,3 +725,8 @@ function asLiteral(value: any): o.Expression {
}
return o.literal(value, o.INFERRED_TYPE);
}

function mapToExpression(map: {[key: string]: any}): o.Expression {
return o.literalMap(Object.getOwnPropertyNames(map).map(
key => ({key, quoted: false, value: o.literal(map[key])})));
}
90 changes: 90 additions & 0 deletions packages/compiler/test/render3/r3_view_compiler_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ describe('r3_view_compiler', () => {
IDENT.ɵe();
IDENT.ɵT(3, '!');
}
ChildComponent.ngComponentDef.h(1, 0);
SomeDirective.ngDirectiveDef.h(2, 0);
IDENT.ɵr(1, 0);
IDENT.ɵr(2, 0);
}
Expand Down Expand Up @@ -271,6 +273,7 @@ describe('r3_view_compiler', () => {
IDENT.ɵe();
}
const IDENT = IDENT.ɵm(1);
IfDirective.ngDirectiveDef.h(3,2);
IDENT.ɵcR(2);
IDENT.ɵr(3,2);
IDENT.ɵcr();
Expand Down Expand Up @@ -414,6 +417,90 @@ describe('r3_view_compiler', () => {
expectEmit(source, locals, 'Incorrect locals constant definition');
});

describe('lifecycle hooks', () => {
const files = {
app: {
'spec.ts': `
import {Component, Input, NgModule} from '@angular/core';

let events: string[] = [];

@Component({selector: 'lifecycle-comp', template: ''})
export class LifecycleComp {
@Input('name') nameMin: string;

ngOnChanges() { events.push('changes' + this.nameMin); }

ngOnInit() { events.push('init' + this.nameMin); }
ngDoCheck() { events.push('check' + this.nameMin); }

ngAfterContentInit() { events.push('content init' + this.nameMin); }
ngAfterContentChecked() { events.push('content check' + this.nameMin); }

ngAfterViewInit() { events.push('view init' + this.nameMin); }
ngAfterViewChecked() { events.push('view check' + this.nameMin); }

ngOnDestroy() { events.push(this.nameMin); }
}

@Component({
selector: 'simple-layout',
template: \`
<lifecycle-comp [name]="name1"></lifecycle-comp>
<lifecycle-comp [name]="name2"></lifecycle-comp>
\`
})
export class SimpleLayout {
name1 = '1';
name2 = '2';
}

@NgModule({declarations: [LifecycleComp, SimpleLayout]}
export class LifecycleModule {}
`
}
};

it('should gen hooks with a few simple components', () => {
const LifecycleCompDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
type: LifecycleComp,
tag: 'lifecycle-comp',
factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
template: function LifecycleComp_Template(ctx: any, cm: boolean) {},
inputs: {nameMin: 'name'},
features: [IDENT.ɵNgOnChangesFeature(LifecycleComp)]
});`;

const SimpleLayoutDefinition = `
static ngComponentDef = IDENT.ɵdefineComponent({
type: SimpleLayout,
tag: 'simple-layout',
factory: function SimpleLayout_Factory() { return new SimpleLayout(); },
template: function SimpleLayout_Template(ctx: any, cm: boolean) {
if (cm) {
IDENT.ɵE(0, LifecycleComp);
IDENT.ɵe();
IDENT.ɵE(2, LifecycleComp);
IDENT.ɵe();
}
IDENT.ɵp(0, 'name', IDENT.ɵb(ctx.name1));
IDENT.ɵp(2, 'name', IDENT.ɵb(ctx.name2));
IDENT.h(1, 0);
IDENT.h(3, 2);
IDENT.ɵr(1, 0);
IDENT.ɵr(3, 2);
}
});`;

const result = compile(files, angularFiles);
const source = result.source;

expectEmit(source, LifecycleCompDefinition, 'Invalid LifecycleComp definition');
expectEmit(source, SimpleLayoutDefinition, 'Invalid SimpleLayout definition');
});
});

describe('template variables', () => {
const shared = {
shared: {
Expand Down Expand Up @@ -516,6 +603,7 @@ describe('r3_view_compiler', () => {
IDENT.ɵe();
}
IDENT.ɵp(1, 'forOf', IDENT.ɵb(ctx.items));
ForOfDirective.ngDirectiveDef.h(2, 1);
IDENT.ɵcR(1);
IDENT.ɵr(2, 1);
IDENT.ɵcr();
Expand Down Expand Up @@ -590,6 +678,7 @@ describe('r3_view_compiler', () => {
IDENT.ɵe();
}
IDENT.ɵp(1, 'forOf', IDENT.ɵb(ctx.items));
IDENT.h(2,1);
IDENT.ɵcR(1);
IDENT.ɵr(2, 1);
IDENT.ɵcr();
Expand All @@ -607,6 +696,7 @@ describe('r3_view_compiler', () => {
}
const IDENT = ctx0.$implicit;
IDENT.ɵp(4, 'forOf', IDENT.ɵb(IDENT.infos));
IDENT.h(5,4);
IDENT.ɵt(2, IDENT.ɵb1('', IDENT.name, ''));
IDENT.ɵcR(4);
IDENT.ɵr(5, 4);
Expand Down