From 2d759927d4a9757d1605c881ef81dac364e5d16f Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Wed, 8 Aug 2018 10:21:20 +0200 Subject: [PATCH] feat(ivy): add support for ng-container in the compiler (#25383) PR Close #25383 --- .../compliance/r3_compiler_compliance_spec.ts | 70 +++++++++++++++++++ .../compiler/src/render3/r3_identifiers.ts | 4 ++ .../compiler/src/render3/view/template.ts | 25 ++++--- packages/core/src/render3/index.ts | 3 + packages/core/src/render3/jit/environment.ts | 2 + 5 files changed, 94 insertions(+), 10 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 0550a4c64b..e8840b85e8 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -222,6 +222,76 @@ describe('compiler compliance', () => { expectEmit(result.source, template, 'Incorrect template'); }); + it('should support ', () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'my-component', + template: \`in a container\` + }) + export class MyComponent {} + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + // The template should look like this (where IDENT is a wild card for an identifier): + const template = ` + … + template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵEC(0); + i0.ɵE(1, "span"); + i0.ɵT(2, "in a "); + i0.ɵe(); + i0.ɵT(3, "container"); + i0.ɵeC(); + } + } + `; + + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); + + it('should generate elementContainerStart/End instructions for empty ', () => { + const files = { + app: { + 'spec.ts': ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'my-component', + template: \`\` + }) + export class MyComponent {} + + @NgModule({declarations: [MyComponent]}) + export class MyModule {} + ` + } + }; + + // The template should look like this (where IDENT is a wild card for an identifier): + const template = ` + … + template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵEC(0); + i0.ɵeC(); + } + } + `; + + const result = compile(files, angularFiles); + expectEmit(result.source, template, 'Incorrect template'); + }); + it('should bind to element properties', () => { const files = { app: { diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index f68d4a0ccc..7a96109e62 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -35,6 +35,10 @@ export class Identifiers { static elementClassProp: o.ExternalReference = {name: 'ɵcp', moduleName: CORE}; + static elementContainerStart: o.ExternalReference = {name: 'ɵEC', moduleName: CORE}; + + static elementContainerEnd: o.ExternalReference = {name: 'ɵeC', moduleName: CORE}; + static elementStyling: o.ExternalReference = {name: 'ɵs', moduleName: CORE}; static elementStylingMap: o.ExternalReference = {name: 'ɵsm', moduleName: CORE}; diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index b47609cde1..be1add72d9 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -17,7 +17,7 @@ import * as html from '../../ml_parser/ast'; import {HtmlParser} from '../../ml_parser/html_parser'; import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; -import {splitNsName} from '../../ml_parser/tags'; +import {isNgContainer as checkIsNgContainer, splitNsName} from '../../ml_parser/tags'; import * as o from '../../output/output_ast'; import {ParseError, ParseSourceSpan} from '../../parse_util'; import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; @@ -276,6 +276,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver let i18nMeta: string = ''; const [namespaceKey, elementName] = splitNsName(element.name); + const isNgContainer = checkIsNgContainer(element.name); // Elements inside i18n sections are replaced with placeholders // TODO(vicb): nested elements are a WIP in this phase @@ -314,11 +315,11 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver selector, (sel: CssSelector, staticType: any) => { this.directives.add(staticType); }); } - // Element creation mode - const parameters: o.Expression[] = [ - o.literal(elementIndex), - o.literal(elementName), - ]; + // Regular element or ng-container creation mode + const parameters: o.Expression[] = [o.literal(elementIndex)]; + if (!isNgContainer) { + parameters.push(o.literal(elementName)); + } // Add the attributes const attributes: o.Expression[] = []; @@ -487,13 +488,15 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver const implicit = o.variable(CONTEXT_NAME); - const createSelfClosingInstruction = - !hasStylingInstructions && element.children.length === 0 && element.outputs.length === 0; + const createSelfClosingInstruction = !hasStylingInstructions && !isNgContainer && + element.children.length === 0 && element.outputs.length === 0; if (createSelfClosingInstruction) { this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters)); } else { - this.creationInstruction(element.sourceSpan, R3.elementStart, trimTrailingNulls(parameters)); + this.creationInstruction( + element.sourceSpan, isNgContainer ? R3.elementContainerStart : R3.elementStart, + trimTrailingNulls(parameters)); // initial styling for static style="..." attributes if (hasStylingInstructions) { @@ -671,7 +674,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver if (!createSelfClosingInstruction) { // Finish element construction mode. - this.creationInstruction(element.endSourceSpan || element.sourceSpan, R3.elementEnd); + this.creationInstruction( + element.endSourceSpan || element.sourceSpan, + isNgContainer ? R3.elementContainerEnd : R3.elementEnd); } // Restore the state before exiting this node diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 841c7500b3..27b380c78c 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -57,6 +57,9 @@ export { elementProperty as p, elementStart as E, + elementContainerStart as EC, + elementContainerEnd as eC, + elementStyling as s, elementStylingMap as sm, elementStyleProp as sp, diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 2c7abc0125..f35b926eff 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -50,6 +50,8 @@ export const angularCoreEnv: {[name: string]: Function} = { 'ɵE': r3.E, 'ɵe': r3.e, 'ɵEe': r3.Ee, + 'ɵEC': r3.EC, + 'ɵeC': r3.eC, 'ɵf0': r3.f0, 'ɵf1': r3.f1, 'ɵf2': r3.f2,