fix(compiler): fix for element needing implicit parent placed in top-level ng-container

fixes #18314
This commit is contained in:
Victor Berchet 2017-07-26 14:53:59 -07:00 committed by Alex Rickabaugh
parent ebef5e697a
commit 381471d338
3 changed files with 285 additions and 281 deletions

View File

@ -230,14 +230,11 @@ class _TreeBuilder {
} }
private _closeVoidElement(): void { private _closeVoidElement(): void {
if (this._elementStack.length > 0) { const el = this._getParentElement();
const el = this._elementStack[this._elementStack.length - 1]; if (el && this.getTagDefinition(el.name).isVoid) {
if (this.getTagDefinition(el.name).isVoid) {
this._elementStack.pop(); this._elementStack.pop();
} }
} }
}
private _consumeStartTag(startTagToken: lex.Token) { private _consumeStartTag(startTagToken: lex.Token) {
const prefix = startTagToken.parts[0]; const prefix = startTagToken.parts[0];
@ -274,12 +271,11 @@ class _TreeBuilder {
} }
private _pushElement(el: html.Element) { private _pushElement(el: html.Element) {
if (this._elementStack.length > 0) { const parentEl = this._getParentElement();
const parentEl = this._elementStack[this._elementStack.length - 1];
if (this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) { if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
this._elementStack.pop(); this._elementStack.pop();
} }
}
const tagDef = this.getTagDefinition(el.name); const tagDef = this.getTagDefinition(el.name);
const {parent, container} = this._getParentElementSkippingContainers(); const {parent, container} = this._getParentElementSkippingContainers();
@ -353,7 +349,7 @@ class _TreeBuilder {
* `<ng-container>` elements are skipped as they are not rendered as DOM element. * `<ng-container>` elements are skipped as they are not rendered as DOM element.
*/ */
private _getParentElementSkippingContainers(): private _getParentElementSkippingContainers():
{parent: html.Element, container: html.Element|null} { {parent: html.Element | null, container: html.Element|null} {
let container: html.Element|null = null; let container: html.Element|null = null;
for (let i = this._elementStack.length - 1; i >= 0; i--) { for (let i = this._elementStack.length - 1; i >= 0; i--) {
@ -363,7 +359,7 @@ class _TreeBuilder {
container = this._elementStack[i]; container = this._elementStack[i];
} }
return {parent: this._elementStack[this._elementStack.length - 1], container}; return {parent: null, container};
} }
private _addToParent(node: html.Node) { private _addToParent(node: html.Node) {

View File

@ -56,24 +56,22 @@ export function isNgTemplate(tagName: string): boolean {
return splitNsName(tagName)[1] === 'ng-template'; return splitNsName(tagName)[1] === 'ng-template';
} }
export function getNsPrefix(fullName: string): string export function getNsPrefix(fullName: string): string;
export function getNsPrefix(fullName: null): null; export function getNsPrefix(fullName: null): null;
export function getNsPrefix(fullName: string | null): string | export function getNsPrefix(fullName: string | null): string|null {
null {
return fullName === null ? null : splitNsName(fullName)[0]; return fullName === null ? null : splitNsName(fullName)[0];
} }
export function mergeNsAndName(prefix: string, localName: string): export function mergeNsAndName(prefix: string, localName: string): string {
string {
return prefix ? `:${prefix}:${localName}` : localName; return prefix ? `:${prefix}:${localName}` : localName;
} }
// see http://www.w3.org/TR/html51/syntax.html#named-character-references // see http://www.w3.org/TR/html51/syntax.html#named-character-references
// see https://html.spec.whatwg.org/multipage/entities.json // see https://html.spec.whatwg.org/multipage/entities.json
// This list is not exhaustive to keep the compiler footprint low. // This list is not exhaustive to keep the compiler footprint low.
// The `&#123;` / `&#x1ab;` syntax should be used when the named character reference does not // The `&#123;` / `&#x1ab;` syntax should be used when the named character reference does not
// exist. // exist.
export const NAMED_ENTITIES: {[k: string]: string} = { export const NAMED_ENTITIES: {[k: string]: string} = {
'Aacute': '\u00C1', 'Aacute': '\u00C1',
'aacute': '\u00E1', 'aacute': '\u00E1',
'Acirc': '\u00C2', 'Acirc': '\u00C2',
@ -326,4 +324,4 @@ export function getNsPrefix(fullName: null): null;
'zeta': '\u03B6', 'zeta': '\u03B6',
'zwj': '\u200D', 'zwj': '\u200D',
'zwnj': '\u200C', 'zwnj': '\u200C',
}; };

View File

@ -152,6 +152,16 @@ export function main() {
]); ]);
}); });
it('should append the required parent considering top level ng-container', () => {
expect(humanizeDom(
parser.parse('<ng-container><tr></tr></ng-container><p></p>', 'TestComp')))
.toEqual([
[html.Element, 'ng-container', 0],
[html.Element, 'tr', 1],
[html.Element, 'p', 0],
]);
});
it('should special case ng-container when adding a required parent', () => { it('should special case ng-container when adding a required parent', () => {
expect(humanizeDom(parser.parse( expect(humanizeDom(parser.parse(
'<table><thead><ng-container><tr></tr></ng-container></thead></table>', '<table><thead><ng-container><tr></tr></ng-container></thead></table>',