feat(ivy): add support for ng-container in the compiler (#25383)

PR Close #25383
This commit is contained in:
Pawel Kozlowski 2018-08-08 10:21:20 +02:00 committed by Ben Lesh
parent 7058072ff6
commit 2d759927d4
5 changed files with 94 additions and 10 deletions

View File

@ -222,6 +222,76 @@ describe('compiler compliance', () => {
expectEmit(result.source, template, 'Incorrect template'); expectEmit(result.source, template, 'Incorrect template');
}); });
it('should support <ng-container>', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<ng-container><span>in a </span>container</ng-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 <ng-container>', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<ng-container></ng-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.ɵeC();
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should bind to element properties', () => { it('should bind to element properties', () => {
const files = { const files = {
app: { app: {

View File

@ -35,6 +35,10 @@ export class Identifiers {
static elementClassProp: o.ExternalReference = {name: 'ɵcp', moduleName: CORE}; 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 elementStyling: o.ExternalReference = {name: 'ɵs', moduleName: CORE};
static elementStylingMap: o.ExternalReference = {name: 'ɵsm', moduleName: CORE}; static elementStylingMap: o.ExternalReference = {name: 'ɵsm', moduleName: CORE};

View File

@ -17,7 +17,7 @@ import * as html from '../../ml_parser/ast';
import {HtmlParser} from '../../ml_parser/html_parser'; import {HtmlParser} from '../../ml_parser/html_parser';
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces'; import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; 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 * as o from '../../output/output_ast';
import {ParseError, ParseSourceSpan} from '../../parse_util'; import {ParseError, ParseSourceSpan} from '../../parse_util';
import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry'; import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry';
@ -276,6 +276,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
let i18nMeta: string = ''; let i18nMeta: string = '';
const [namespaceKey, elementName] = splitNsName(element.name); const [namespaceKey, elementName] = splitNsName(element.name);
const isNgContainer = checkIsNgContainer(element.name);
// Elements inside i18n sections are replaced with placeholders // Elements inside i18n sections are replaced with placeholders
// TODO(vicb): nested elements are a WIP in this phase // TODO(vicb): nested elements are a WIP in this phase
@ -314,11 +315,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
selector, (sel: CssSelector, staticType: any) => { this.directives.add(staticType); }); selector, (sel: CssSelector, staticType: any) => { this.directives.add(staticType); });
} }
// Element creation mode // Regular element or ng-container creation mode
const parameters: o.Expression[] = [ const parameters: o.Expression[] = [o.literal(elementIndex)];
o.literal(elementIndex), if (!isNgContainer) {
o.literal(elementName), parameters.push(o.literal(elementName));
]; }
// Add the attributes // Add the attributes
const attributes: o.Expression[] = []; const attributes: o.Expression[] = [];
@ -487,13 +488,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const implicit = o.variable(CONTEXT_NAME); const implicit = o.variable(CONTEXT_NAME);
const createSelfClosingInstruction = const createSelfClosingInstruction = !hasStylingInstructions && !isNgContainer &&
!hasStylingInstructions && element.children.length === 0 && element.outputs.length === 0; element.children.length === 0 && element.outputs.length === 0;
if (createSelfClosingInstruction) { if (createSelfClosingInstruction) {
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters)); this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
} else { } 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 // initial styling for static style="..." attributes
if (hasStylingInstructions) { if (hasStylingInstructions) {
@ -671,7 +674,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
if (!createSelfClosingInstruction) { if (!createSelfClosingInstruction) {
// Finish element construction mode. // 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 // Restore the state before exiting this node

View File

@ -57,6 +57,9 @@ export {
elementProperty as p, elementProperty as p,
elementStart as E, elementStart as E,
elementContainerStart as EC,
elementContainerEnd as eC,
elementStyling as s, elementStyling as s,
elementStylingMap as sm, elementStylingMap as sm,
elementStyleProp as sp, elementStyleProp as sp,

View File

@ -50,6 +50,8 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵE': r3.E, 'ɵE': r3.E,
'ɵe': r3.e, 'ɵe': r3.e,
'ɵEe': r3.Ee, 'ɵEe': r3.Ee,
'ɵEC': r3.EC,
'ɵeC': r3.eC,
'ɵf0': r3.f0, 'ɵf0': r3.f0,
'ɵf1': r3.f1, 'ɵf1': r3.f1,
'ɵf2': r3.f2, 'ɵf2': r3.f2,