From 19dfadb7177dd9c7face61e219e331ddca631f4a Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 22 Apr 2019 12:48:06 +0200 Subject: [PATCH] fix(ivy): include context name for template functions for `ng-content` (#30025) Previously, a template's context name would only be included in an embedded template function if the element that the template was declared on has a tag name. This is generally true for elements, except for `ng-content` that does not have a tag name. By omitting the context name the compiler could introduce duplicate template function names, which would fail at runtime. This commit fixes the behavior by always including the context name in the template function's name, regardless of tag name. Resolves FW-1272 PR Close #30025 --- .../r3_view_compiler_template_spec.ts | 48 +++++++++++++++++++ .../compiler/src/render3/view/template.ts | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) 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 cfed15fd20..09d702fc86 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 @@ -592,6 +592,54 @@ describe('compiler compliance: template', () => { expect(allTemplateFunctionsNames).toEqual(uniqueTemplateFunctionNames); }); + it('should create unique template function names for ng-content templates', () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'a-component', + template: \` + + \`, + }) + export class AComponent { + show = true; + } + + @Component({ + selector: 'b-component', + template: \` + + \`, + }) + export class BComponent { + show = true; + } + + @NgModule({declarations: [AComponent, BComponent]}) + export class AModule {} + ` + }, + }; + + const result = compile(files, angularFiles); + + const allTemplateFunctionsNames = (result.source.match(/function ([^\s(]+)/g) || []) + .map(x => x.slice(9)) + .filter(x => x.includes('Template')) + .sort(); + const uniqueTemplateFunctionNames = Array.from(new Set(allTemplateFunctionsNames)); + + // Expected template function: + // - 1 for AComponent's template. + // - 1 for BComponent's template. + // - 2 for the two components. + expect(allTemplateFunctionsNames.length).toBe(1 + 1 + 2); + expect(allTemplateFunctionsNames).toEqual(uniqueTemplateFunctionNames); + }); + it('should create unique listener function names even for similar nested template structures', () => { const files = { diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index aa8a4a63f3..30b9f5f0db 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -822,7 +822,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } const tagName = sanitizeIdentifier(template.tagName || ''); - const contextName = `${tagName ? this.contextName + '_' + tagName : ''}_${templateIndex}`; + const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`; const templateName = `${contextName}_Template`; const parameters: o.Expression[] = [