Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions modules/angular2/src/compiler/template_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,29 +264,40 @@ class TemplateParseVisitor implements HtmlAstVisitor {
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
var children = htmlVisitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this,
element.children, Component.create(directives));
var elementNgContentIndex =
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);

// Override the actual selector when the `ngProjectAs` attribute is provided
var projectionSelector = isPresent(preparsedElement.projectAs) ?
CssSelector.parse(preparsedElement.projectAs)[0] :
elementCssSelector;
var ngContentIndex = component.findNgContentIndex(projectionSelector);
var parsedElement;

if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
if (isPresent(element.children) && element.children.length > 0) {
this._reportError(
`<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content>`,
element.sourceSpan);
}
parsedElement =
new NgContentAst(this.ngContentCount++, elementNgContentIndex, element.sourceSpan);

parsedElement = new NgContentAst(
this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
} else if (isTemplateElement) {
this._assertAllEventsPublishedByDirectives(directives, events);
this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps,
element.sourceSpan);
parsedElement = new EmbeddedTemplateAst(attrs, events, vars, directives, children,
elementNgContentIndex, element.sourceSpan);

parsedElement =
new EmbeddedTemplateAst(attrs, events, vars, directives, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
} else {
this._assertOnlyOneComponent(directives, element.sourceSpan);
var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
let ngContentIndex =
hasInlineTemplates ? null : component.findNgContentIndex(projectionSelector);

parsedElement =
new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives,
children, elementNgContentIndex, element.sourceSpan);
children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
}
if (hasInlineTemplates) {
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
Expand All @@ -297,9 +308,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
element.name, templateElementOrDirectiveProps, templateDirectives);
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps,
element.sourceSpan);
parsedElement = new EmbeddedTemplateAst(
[], [], templateVars, templateDirectives, [parsedElement],
component.findNgContentIndex(templateCssSelector), element.sourceSpan);

parsedElement = new EmbeddedTemplateAst([], [], templateVars, templateDirectives,
[parsedElement], ngContentIndex, element.sourceSpan);
}
return parsedElement;
}
Expand Down
10 changes: 8 additions & 2 deletions modules/angular2/src/compiler/template_preparser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ const LINK_STYLE_REL_VALUE = 'stylesheet';
const STYLE_ELEMENT = 'style';
const SCRIPT_ELEMENT = 'script';
const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
const NG_PROJECT_AS = 'ngProjectAs';

export function preparseElement(ast: HtmlElementAst): PreparsedElement {
var selectAttr = null;
var hrefAttr = null;
var relAttr = null;
var nonBindable = false;
var projectAs: string = null;
ast.attrs.forEach(attr => {
let lcAttrName = attr.name.toLowerCase();
if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
Expand All @@ -27,6 +29,10 @@ export function preparseElement(ast: HtmlElementAst): PreparsedElement {
relAttr = attr.value;
} else if (attr.name == NG_NON_BINDABLE_ATTR) {
nonBindable = true;
} else if (attr.name == NG_PROJECT_AS) {
if (attr.value.length > 0) {
projectAs = attr.value;
}
}
});
selectAttr = normalizeNgContentSelect(selectAttr);
Expand All @@ -41,7 +47,7 @@ export function preparseElement(ast: HtmlElementAst): PreparsedElement {
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
type = PreparsedElementType.STYLESHEET;
}
return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable);
return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
}

export enum PreparsedElementType {
Expand All @@ -54,7 +60,7 @@ export enum PreparsedElementType {

export class PreparsedElement {
constructor(public type: PreparsedElementType, public selectAttr: string, public hrefAttr: string,
public nonBindable: boolean) {}
public nonBindable: boolean, public projectAs: string) {}
}


Expand Down
33 changes: 33 additions & 0 deletions modules/angular2/test/compiler/template_parser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,39 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
[createComp('div', ['*'])])))
.toEqual([['div', null], ['#text({{hello}})', 0], ['span', 0]]);
});

it('should match the element when there is an inline template', () => {
expect(humanizeContentProjection(
parse('<div><b *ngIf="cond"></b></div>', [createComp('div', ['a', 'b']), ngIf])))
.toEqual([['div', null], ['template', 1], ['b', null]]);
});

describe('ngProjectAs', () => {
it('should override elements', () => {
expect(humanizeContentProjection(
parse('<div><a ngProjectAs="b"></a></div>', [createComp('div', ['a', 'b'])])))
.toEqual([['div', null], ['a', 1]]);
});

it('should override <ng-content>', () => {
expect(humanizeContentProjection(
parse('<div><ng-content ngProjectAs="b"></ng-content></div>',
[createComp('div', ['ng-content', 'b'])])))
.toEqual([['div', null], ['ng-content', 1]]);
});

it('should override <template>', () => {
expect(humanizeContentProjection(parse('<div><template ngProjectAs="b"></template></div>',
[createComp('div', ['template', 'b'])])))
.toEqual([['div', null], ['template', 1]]);
});

it('should override inline templates', () => {
expect(humanizeContentProjection(parse('<div><a *ngIf="cond" ngProjectAs="b"></a></div>',
[createComp('div', ['a', 'b']), ngIf])))
.toEqual([['div', null], ['template', 1], ['a', null]]);
});
});
});

describe('splitClasses', () => {
Expand Down
5 changes: 4 additions & 1 deletion modules/angular2/test/compiler/template_preparser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function main() {
beforeEach(inject([HtmlParser], (_htmlParser: HtmlParser) => { htmlParser = _htmlParser; }));

function preparse(html: string): PreparsedElement {
return preparseElement(htmlParser.parse(html, '').rootNodes[0]);
return preparseElement(htmlParser.parse(html, 'TestComp').rootNodes[0]);
}

it('should detect script elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
Expand Down Expand Up @@ -54,5 +54,8 @@ export function main() {
expect(preparse('<ng-content select="*">').selectAttr).toEqual('*');
}));

it('should extract ngProjectAs value', () => {
expect(preparse('<p ngProjectAs="el[attr].class"></p>').projectAs).toEqual('el[attr].class');
});
});
}