fix(ivy): adding projectDef instructions to all templates where <ng-content> is present (FW-745) (#27384)

Prior to this change `projectDef` instructions were placed to root templates only, thus the necessary information (selectors) in nested templates was missing. This update adds the logic to insert `projectDef` instructions to all templates where <ng-content> is present.

PR Close #27384
This commit is contained in:
Andrew Kushnir
2018-11-30 15:01:37 -08:00
committed by Igor Minar
parent 8e644d99fc
commit f0b0d64453
7 changed files with 132 additions and 105 deletions

View File

@ -45,16 +45,10 @@ const IDENT_PROPERTY_IDX = 9;
const IDENT_EVENT_IDX = 10;
const TEMPLATE_ATTR_PREFIX = '*';
// Default selector used by `<ng-content>` if none specified
const DEFAULT_CONTENT_SELECTOR = '*';
// Result of the html AST to Ivy AST transformation
export type Render3ParseResult = {
nodes: t.Node[]; errors: ParseError[];
// Any non default (empty or '*') selector found in the template
ngContentSelectors: string[];
// Wether the template contains any `<ng-content>`
hasNgContent: boolean;
};
export function htmlAstToRender3Ast(
@ -74,17 +68,11 @@ export function htmlAstToRender3Ast(
return {
nodes: ivyNodes,
errors: allErrors,
ngContentSelectors: transformer.ngContentSelectors,
hasNgContent: transformer.hasNgContent,
};
}
class HtmlAstToIvyAst implements html.Visitor {
errors: ParseError[] = [];
// Selectors for the `ng-content` tags. Only non `*` selectors are recorded here
ngContentSelectors: string[] = [];
// Any `<ng-content>` in the template ?
hasNgContent = false;
constructor(private bindingParser: BindingParser) {}
@ -168,20 +156,12 @@ class HtmlAstToIvyAst implements html.Visitor {
let parsedElement: t.Node|undefined;
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
// `<ng-content>`
this.hasNgContent = true;
if (element.children && !element.children.every(isEmptyTextNode)) {
this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
}
const selector = preparsedElement.selectAttr;
let attributes: t.TextAttribute[] =
element.attrs.map(attribute => this.visitAttribute(attribute));
const selectorIndex =
selector === DEFAULT_CONTENT_SELECTOR ? 0 : this.ngContentSelectors.push(selector);
parsedElement = new t.Content(selectorIndex, attributes, element.sourceSpan, element.i18n);
const attrs: t.TextAttribute[] = element.attrs.map(attr => this.visitAttribute(attr));
parsedElement = new t.Content(selector, attrs, element.sourceSpan, element.i18n);
} else if (isTemplateElement) {
// `<ng-template>`
const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);