fix(ivy): generate lifecycle pattern (#21865)
Implement the lifecycle pattern defined in #21793 PR Close #21865
This commit is contained in:
		
							parent
							
								
									d3c2aa5f95
								
							
						
					
					
						commit
						f816666ede
					
				| @ -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'; | ||||
| @ -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'; | ||||
| 
 | ||||
| @ -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}`); | ||||
| 
 | ||||
| @ -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)]`
 | ||||
|   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}`); | ||||
| @ -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) | ||||
| @ -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, | ||||
| @ -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), | ||||
| @ -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])}))); | ||||
| } | ||||
|  | ||||
| @ -237,6 +237,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); | ||||
|           } | ||||
| @ -300,6 +302,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(); | ||||
| @ -443,6 +446,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: { | ||||
| @ -545,6 +632,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(); | ||||
| @ -619,6 +707,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(); | ||||
| @ -636,6 +725,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); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user