From 11e2d9da1ac79e466be32c9829744075a3db7a20 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Mon, 20 Aug 2018 14:23:17 +0200 Subject: [PATCH] feat(ivy): add support to template local refs in the compiler (#25576) Fixes #23316 PR Close #25576 --- .../r3_view_compiler_template_spec.ts | 41 ++++++++++++ .../compiler/src/render3/r3_identifiers.ts | 3 + .../compiler/src/render3/view/template.ts | 62 ++++++++++++------- packages/core/src/render3/jit/environment.ts | 1 + 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts index 8d6ee28ced..9e2503fd51 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts @@ -359,4 +359,45 @@ describe('compiler compliance: template', () => { expectEmit(result.source, template, 'Incorrect template'); }); + + it('should support local refs on ', () => { + + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'my-component', + template: 'some-content'; + }) + export class MyComponent {} + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + const template = ` + const _c0 = ["foo", ""]; + + function Template_0(rf, ctx) { + if (rf & 1) { + $i0$.ɵtext(0, "some-content"); + } + } + + // ... + + template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $i0$.ɵtemplate(0, Template_0, 1, null, null, _c0, i0.ɵtemplateRefExtractor); + } + }`; + + const result = compile(files, angularFiles); + + expectEmit(result.source, template, 'Incorrect template'); + }); }); diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 47467a7c72..c66d6fac30 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -118,6 +118,9 @@ export class Identifiers { static directiveInject: o.ExternalReference = {name: 'ɵdirectiveInject', moduleName: CORE}; + static templateRefExtractor: + o.ExternalReference = {name: 'ɵtemplateRefExtractor', moduleName: CORE}; + static defineBase: o.ExternalReference = {name: 'ɵdefineBase', moduleName: CORE}; static BaseDef: o.ExternalReference = { diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 2d4bf159b3..e0ad56d1b5 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -457,30 +457,8 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver o.TYPED_NULL_EXPR; parameters.push(attrArg); - if (element.references && element.references.length > 0) { - const references = flatten(element.references.map(reference => { - const slot = this.allocateDataSlot(); - // Generate the update temporary. - const variableName = this._bindingScope.freshReferenceName(); - const retrievalLevel = this.level; - const lhs = o.variable(variableName); - this._bindingScope.set( - retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT, - (scope: BindingScope, relativeLevel: number) => { - // e.g. x(2); - const nextContextStmt = - relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : []; - - // e.g. const $foo$ = r(1); - const refExpr = lhs.set(o.importExpr(R3.reference).callFn([o.literal(slot)])); - return nextContextStmt.concat(refExpr.toConstDecl()); - }); - return [reference.name, reference.value]; - })); - parameters.push(this.constantPool.getConstLiteral(asLiteral(references), true)); - } else { - parameters.push(o.TYPED_NULL_EXPR); - } + // local refs (ex.:
) + parameters.push(this.prepareRefsParameter(element.references)); const wasInNamespace = this._namespace; const currentNamespace = this.getNamespaceInstruction(namespaceKey); @@ -730,6 +708,14 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver if (attributeNames.length) { parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true)); + } else { + parameters.push(o.TYPED_NULL_EXPR); + } + + // local refs (ex.: ) + if (template.references && template.references.length) { + parameters.push(this.prepareRefsParameter(template.references)); + parameters.push(o.importExpr(R3.templateRefExtractor)); } // e.g. p(1, 'forOf', ɵbind(ctx.items)); @@ -858,6 +844,34 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver return value instanceof Interpolation || skipBindFn ? valExpr : o.importExpr(R3.bind).callFn([valExpr]); } + + private prepareRefsParameter(references: t.Reference[]): o.Expression { + if (!references || references.length === 0) { + return o.TYPED_NULL_EXPR; + } + + const refsParam = flatten(references.map(reference => { + const slot = this.allocateDataSlot(); + // Generate the update temporary. + const variableName = this._bindingScope.freshReferenceName(); + const retrievalLevel = this.level; + const lhs = o.variable(variableName); + this._bindingScope.set( + retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT, + (scope: BindingScope, relativeLevel: number) => { + // e.g. x(2); + const nextContextStmt = + relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : []; + + // e.g. const $foo$ = r(1); + const refExpr = lhs.set(o.importExpr(R3.reference).callFn([o.literal(slot)])); + return nextContextStmt.concat(refExpr.toConstDecl()); + }); + return [reference.name, reference.value]; + })); + + return this.constantPool.getConstLiteral(asLiteral(refsParam), true); + } } class ValueConverter extends AstMemoryEfficientTransformer { diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 0279767c57..87b490bbb8 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -34,6 +34,7 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵinjectElementRef': r3.injectElementRef, 'ɵinjectTemplateRef': r3.injectTemplateRef, 'ɵinjectViewContainerRef': r3.injectViewContainerRef, + 'ɵtemplateRefExtractor': r3.templateRefExtractor, 'ɵNgOnChangesFeature': r3.NgOnChangesFeature, 'ɵPublicFeature': r3.PublicFeature, 'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,