feat(ivy): add support for ng-container in the compiler (#25383)
PR Close #25383
This commit is contained in:
parent
7058072ff6
commit
2d759927d4
|
@ -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: {
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue