refactor(compiler): make all commands const

Closes #5135
This commit is contained in:
Tobias Bosch 2015-11-02 08:39:14 -08:00
parent fb8b8157ff
commit e667ad3e6b
35 changed files with 1002 additions and 868 deletions

View File

@ -23,7 +23,7 @@ dependencies:
source_span: '^1.0.0' source_span: '^1.0.0'
stack_trace: '^1.1.1' stack_trace: '^1.1.1'
dev_dependencies: dev_dependencies:
guinness: '^0.1.17' guinness: '^0.1.18'
transformers: transformers:
- angular2 - angular2
- $dart2js: - $dart2js:

View File

@ -63,7 +63,7 @@ export class ChangeDetectionCompiler {
var index = 0; var index = 0;
var sourceParts = changeDetectorDefinitions.map(definition => { var sourceParts = changeDetectorDefinitions.map(definition => {
var codegen: any; var codegen: any;
var sourcePart; var sourcePart: string;
// TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts // TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts
// suffix // suffix
// and have the same API for calling them! // and have the same API for calling them!
@ -74,7 +74,7 @@ export class ChangeDetectionCompiler {
'dynamic' : 'dynamic' :
`${moduleRef(componentType.moduleUrl)}${componentType.name}`; `${moduleRef(componentType.moduleUrl)}${componentType.name}`;
codegen.generate(typeRef, className, definition); codegen.generate(typeRef, className, definition);
factories.push(`(dispatcher) => new ${className}(dispatcher)`); factories.push(`${className}.newChangeDetector`);
sourcePart = codegen.toString(); sourcePart = codegen.toString();
} else { } else {
codegen = new ChangeDetectorJITGenerator( codegen = new ChangeDetectorJITGenerator(

View File

@ -1,15 +1,15 @@
import {isPresent, isBlank, Type, isString, StringWrapper} from 'angular2/src/facade/lang'; import {isPresent, isBlank, Type, isString, StringWrapper, IS_DART} from 'angular2/src/facade/lang';
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection'; import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import { import {
TemplateCmd, TemplateCmd,
text, TextCmd,
ngContent, NgContentCmd,
beginElement, BeginElementCmd,
endElement, EndElementCmd,
beginComponent, BeginComponentCmd,
endComponent, EndComponentCmd,
embeddedTemplate, EmbeddedTemplateCmd,
CompiledTemplate CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import { import {
TemplateAst, TemplateAst,
@ -32,12 +32,11 @@ import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import { import {
shimHostAttribute, escapeSingleQuoteString,
shimContentAttribute, codeGenConstConstructorCall,
shimContentAttributeExpr, codeGenValueFn,
shimHostAttributeExpr MODULE_SUFFIX
} from './style_compiler'; } from './util';
import {escapeSingleQuoteString, MODULE_SUFFIX} from './util';
import {Injectable} from 'angular2/src/core/di'; import {Injectable} from 'angular2/src/core/di';
export var TEMPLATE_COMMANDS_MODULE_REF = export var TEMPLATE_COMMANDS_MODULE_REF =
@ -49,28 +48,24 @@ const STYLE_ATTR = 'style';
@Injectable() @Injectable()
export class CommandCompiler { export class CommandCompiler {
compileComponentRuntime(component: CompileDirectiveMetadata, appId: string, templateId: number, compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
template: TemplateAst[], changeDetectorFactories: Function[], changeDetectorFactories: Function[],
componentTemplateFactory: Function): TemplateCmd[] { componentTemplateFactory: Function): TemplateCmd[] {
var visitor = new CommandBuilderVisitor( var visitor = new CommandBuilderVisitor(
new RuntimeCommandFactory(component, appId, templateId, componentTemplateFactory, new RuntimeCommandFactory(component, componentTemplateFactory, changeDetectorFactories), 0);
changeDetectorFactories),
0);
templateVisitAll(visitor, template); templateVisitAll(visitor, template);
return visitor.result; return visitor.result;
} }
compileComponentCodeGen(component: CompileDirectiveMetadata, appIdExpr: string, compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
templateIdExpr: string, template: TemplateAst[],
changeDetectorFactoryExpressions: string[], changeDetectorFactoryExpressions: string[],
componentTemplateFactory: Function): SourceExpression { componentTemplateFactory: Function): SourceExpression {
var visitor = new CommandBuilderVisitor( var visitor =
new CodegenCommandFactory(component, appIdExpr, templateIdExpr, componentTemplateFactory, new CommandBuilderVisitor(new CodegenCommandFactory(component, componentTemplateFactory,
changeDetectorFactoryExpressions), changeDetectorFactoryExpressions),
0); 0);
templateVisitAll(visitor, template); templateVisitAll(visitor, template);
var source = `[${visitor.result.join(',')}]`; return new SourceExpression([], codeGenArray(visitor.result));
return new SourceExpression([], source);
} }
} }
@ -83,7 +78,7 @@ interface CommandFactory<R> {
createEndElement(): R; createEndElement(): R;
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): R; encapsulation: ViewEncapsulation, ngContentIndex: number): R;
createEndComponent(): R; createEndComponent(): R;
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
@ -91,112 +86,89 @@ interface CommandFactory<R> {
} }
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> { class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
constructor(private component: CompileDirectiveMetadata, private appId: string, constructor(private component: CompileDirectiveMetadata,
private templateId: number, private componentTemplateFactory: Function, private componentTemplateFactory: Function,
private changeDetectorFactories: Function[]) {} private changeDetectorFactories: Function[]) {}
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] { private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
return directives.map(directive => directive.type.runtime); return directives.map(directive => directive.type.runtime);
} }
private _addStyleShimAttributes(attrNameAndValues: string[],
localComponent: CompileDirectiveMetadata,
localTemplateId: number): string[] {
var additionalStyles = [];
if (isPresent(localComponent) &&
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStyles.push(shimHostAttribute(this.appId, localTemplateId));
additionalStyles.push('');
}
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStyles.push(shimContentAttribute(this.appId, this.templateId));
additionalStyles.push('');
}
return additionalStyles.concat(attrNameAndValues);
}
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd { createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
return text(value, isBound, ngContentIndex); return new TextCmd(value, isBound, ngContentIndex);
} }
createNgContent(index: number, ngContentIndex: number): TemplateCmd { createNgContent(index: number, ngContentIndex: number): TemplateCmd {
return ngContent(index, ngContentIndex); return new NgContentCmd(index, ngContentIndex);
} }
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): TemplateCmd { isBound: boolean, ngContentIndex: number): TemplateCmd {
return beginElement(name, this._addStyleShimAttributes(attrNameAndValues, null, null), return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives), this._mapDirectives(directives), isBound, ngContentIndex);
isBound, ngContentIndex);
} }
createEndElement(): TemplateCmd { return endElement(); } createEndElement(): TemplateCmd { return new EndElementCmd(); }
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): TemplateCmd { encapsulation: ViewEncapsulation, ngContentIndex: number): TemplateCmd {
var nestedTemplate = this.componentTemplateFactory(directives[0]); var nestedTemplateAccessor = this.componentTemplateFactory(directives[0]);
return beginComponent( return new BeginComponentCmd(name, attrNameAndValues, eventTargetAndNames,
name, this._addStyleShimAttributes(attrNameAndValues, directives[0], nestedTemplate.id), variableNameAndValues, this._mapDirectives(directives),
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives), nativeShadow, encapsulation, ngContentIndex, nestedTemplateAccessor);
ngContentIndex, nestedTemplate);
} }
createEndComponent(): TemplateCmd { return endComponent(); } createEndComponent(): TemplateCmd { return new EndComponentCmd(); }
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isMerged: boolean, ngContentIndex: number, isMerged: boolean, ngContentIndex: number,
children: TemplateCmd[]): TemplateCmd { children: TemplateCmd[]): TemplateCmd {
return embeddedTemplate(attrNameAndValues, variableNameAndValues, return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues,
this._mapDirectives(directives), isMerged, ngContentIndex, this._mapDirectives(directives), isMerged, ngContentIndex,
this.changeDetectorFactories[embeddedTemplateIndex], children); this.changeDetectorFactories[embeddedTemplateIndex], children);
} }
} }
class CodegenCommandFactory implements CommandFactory<string> { class CodegenCommandFactory implements CommandFactory<Expression> {
constructor(private component: CompileDirectiveMetadata, private appIdExpr: string, constructor(private component: CompileDirectiveMetadata,
private templateIdExpr: string, private componentTemplateFactory: Function, private componentTemplateFactory: Function,
private changeDetectorFactoryExpressions: string[]) {} private changeDetectorFactoryExpressions: string[]) {}
private _addStyleShimAttributes(attrNameAndValues: string[], createText(value: string, isBound: boolean, ngContentIndex: number): Expression {
localComponent: CompileDirectiveMetadata, return new Expression(
localTemplateIdExpr: string): any[] { `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'TextCmd')}(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`);
var additionalStlyes = [];
if (isPresent(localComponent) &&
localComponent.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStlyes.push(
new Expression(shimHostAttributeExpr(this.appIdExpr, localTemplateIdExpr)));
additionalStlyes.push('');
}
if (this.component.template.encapsulation === ViewEncapsulation.Emulated) {
additionalStlyes.push(
new Expression(shimContentAttributeExpr(this.appIdExpr, this.templateIdExpr)));
additionalStlyes.push('');
}
return additionalStlyes.concat(attrNameAndValues);
} }
createNgContent(index: number, ngContentIndex: number): Expression {
createText(value: string, isBound: boolean, ngContentIndex: number): string { return new Expression(
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`; `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'NgContentCmd')}(${index}, ${ngContentIndex})`);
}
createNgContent(index: number, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${index}, ${ngContentIndex})`;
} }
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): string { isBound: boolean, ngContentIndex: number): Expression {
var attrsExpression = codeGenArray(this._addStyleShimAttributes(attrNameAndValues, null, null)); var attrsExpression = codeGenArray(attrNameAndValues);
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`; return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginElementCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
`${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`);
}
createEndElement(): Expression {
return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndElementCmd')}()`);
} }
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): string { encapsulation: ViewEncapsulation, ngContentIndex: number): Expression {
var nestedCompExpr = this.componentTemplateFactory(directives[0]); var attrsExpression = codeGenArray(attrNameAndValues);
var attrsExpression = codeGenArray( return new Expression(
this._addStyleShimAttributes(attrNameAndValues, directives[0], `${nestedCompExpr}.id`)); `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginComponentCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${nativeShadow}, ${ngContentIndex}, ${nestedCompExpr})`; `${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${codeGenViewEncapsulation(encapsulation)}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`);
}
createEndComponent(): Expression {
return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndComponentCmd')}()`);
} }
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[], createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[], variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isMerged: boolean, ngContentIndex: number, children: string[]): string { isMerged: boolean, ngContentIndex: number,
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` + children: Expression[]): Expression {
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`; return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EmbeddedTemplateCmd')}(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, ${codeGenArray(children)})`);
} }
} }
@ -270,7 +242,7 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
if (isPresent(component)) { if (isPresent(component)) {
this.result.push(this.commandFactory.createBeginComponent( this.result.push(this.commandFactory.createBeginComponent(
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives, ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex)); component.template.encapsulation, ast.ngContentIndex));
templateVisitAll(this, ast.children); templateVisitAll(this, ast.children);
this.result.push(this.commandFactory.createEndComponent()); this.result.push(this.commandFactory.createEndComponent());
} else { } else {
@ -383,11 +355,21 @@ function escapeValue(value: any): string {
} }
function codeGenArray(data: any[]): string { function codeGenArray(data: any[]): string {
return `[${data.map(escapeValue).join(',')}]`; var base = `[${data.map(escapeValue).join(',')}]`;
return IS_DART ? `const ${base}` : base;
} }
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string { function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
var expressions = directives.map( var expressions = directives.map(
directiveType => `${moduleRef(directiveType.type.moduleUrl)}${directiveType.type.name}`); directiveType => `${moduleRef(directiveType.type.moduleUrl)}${directiveType.type.name}`);
return `[${expressions.join(',')}]`; var base = `[${expressions.join(',')}]`;
return IS_DART ? `const ${base}` : base;
}
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
if (IS_DART) {
return `${TEMPLATE_COMMANDS_MODULE_REF}${value}`;
} else {
return `${value}`;
}
} }

View File

@ -9,21 +9,12 @@ import {UrlResolver} from 'angular2/src/compiler/url_resolver';
import {extractStyleUrls} from './style_url_resolver'; import {extractStyleUrls} from './style_url_resolver';
import { import {
escapeSingleQuoteString, escapeSingleQuoteString,
codeGenConcatArray,
codeGenMapArray,
codeGenReplaceAll,
codeGenExportVariable, codeGenExportVariable,
codeGenToString, codeGenToString,
MODULE_SUFFIX MODULE_SUFFIX
} from './util'; } from './util';
import {Injectable} from 'angular2/src/core/di'; import {Injectable} from 'angular2/src/core/di';
import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
const COMPONENT_VARIABLE = '%COMP%';
var COMPONENT_REGEX = /%COMP%/g;
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
const HOST_ATTR_EXPR = `'_nghost-'+${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR_EXPR = `'_ngcontent-'+${COMPONENT_VARIABLE}`;
@Injectable() @Injectable()
export class StyleCompiler { export class StyleCompiler {
@ -32,28 +23,16 @@ export class StyleCompiler {
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {} constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
compileComponentRuntime(appId: string, templateId: number, compileComponentRuntime(template: CompileTemplateMetadata): Promise<Array<string | any[]>> {
template: CompileTemplateMetadata): Promise<string[]> {
var styles = template.styles; var styles = template.styles;
var styleAbsUrls = template.styleUrls; var styleAbsUrls = template.styleUrls;
return this._loadStyles(styles, styleAbsUrls, return this._loadStyles(styles, styleAbsUrls,
template.encapsulation === ViewEncapsulation.Emulated) template.encapsulation === ViewEncapsulation.Emulated);
.then(styles => styles.map(style => StringWrapper.replaceAll(
style, COMPONENT_REGEX, componentId(appId, templateId))));
} }
compileComponentCodeGen(appIdExpression: string, templateIdExpression: string, compileComponentCodeGen(template: CompileTemplateMetadata): SourceExpression {
template: CompileTemplateMetadata): SourceExpression {
var shim = template.encapsulation === ViewEncapsulation.Emulated; var shim = template.encapsulation === ViewEncapsulation.Emulated;
var suffix; return this._styleCodeGen(template.styles, template.styleUrls, shim);
if (shim) {
suffix = codeGenMapArray(
['style'],
`style${codeGenReplaceAll(COMPONENT_VARIABLE, componentIdExpression(appIdExpression, templateIdExpression))}`);
} else {
suffix = '';
}
return this._styleCodeGen(template.styles, template.styleUrls, shim, suffix);
} }
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] { compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
@ -61,17 +40,16 @@ export class StyleCompiler {
return [ return [
this._styleModule( this._styleModule(
stylesheetUrl, false, stylesheetUrl, false,
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false, '')), this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false)),
this._styleModule( this._styleModule(stylesheetUrl, true, this._styleCodeGen([styleWithImports.style],
stylesheetUrl, true, styleWithImports.styleUrls, true))
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, true, ''))
]; ];
} }
clearCache() { this._styleCache.clear(); } clearCache() { this._styleCache.clear(); }
private _loadStyles(plainStyles: string[], absUrls: string[], private _loadStyles(plainStyles: string[], absUrls: string[],
encapsulate: boolean): Promise<string[]> { encapsulate: boolean): Promise<Array<string | any[]>> {
var promises = absUrls.map((absUrl) => { var promises = absUrls.map((absUrl) => {
var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`; var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`;
var result = this._styleCache.get(cacheKey); var result = this._styleCache.get(cacheKey);
@ -86,22 +64,23 @@ export class StyleCompiler {
return result; return result;
}); });
return PromiseWrapper.all(promises).then((nestedStyles: string[][]) => { return PromiseWrapper.all(promises).then((nestedStyles: string[][]) => {
var result = plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate)); var result: Array<string | any[]> =
nestedStyles.forEach(styles => styles.forEach(style => result.push(style))); plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
nestedStyles.forEach(styles => result.push(styles));
return result; return result;
}); });
} }
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean, private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean): SourceExpression {
suffix: string): SourceExpression { var arrayPrefix = IS_DART ? `const` : '';
var expressionSource = `(`; var styleExpressions = plainStyles.map(
expressionSource += plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)));
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
for (var i = 0; i < absUrls.length; i++) { for (var i = 0; i < absUrls.length; i++) {
var moduleUrl = this._createModuleUrl(absUrls[i], shim); var moduleUrl = this._createModuleUrl(absUrls[i], shim);
expressionSource += codeGenConcatArray(`${moduleRef(moduleUrl)}STYLES`); styleExpressions.push(`${moduleRef(moduleUrl)}STYLES`);
} }
expressionSource += `)${suffix}`; var expressionSource = `${arrayPrefix} [${styleExpressions.join(',')}]`;
return new SourceExpression([], expressionSource); return new SourceExpression([], expressionSource);
} }
@ -122,29 +101,3 @@ export class StyleCompiler {
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`; return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
} }
} }
export function shimContentAttribute(appId: string, templateId: number): string {
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
}
export function shimContentAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
return StringWrapper.replaceAll(CONTENT_ATTR_EXPR, COMPONENT_REGEX,
componentIdExpression(appIdExpr, templateIdExpr));
}
export function shimHostAttribute(appId: string, templateId: number): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentId(appId, templateId));
}
export function shimHostAttributeExpr(appIdExpr: string, templateIdExpr: string): string {
return StringWrapper.replaceAll(HOST_ATTR_EXPR, COMPONENT_REGEX,
componentIdExpression(appIdExpr, templateIdExpr));
}
function componentId(appId: string, templateId: number): string {
return `${appId}-${templateId}`;
}
function componentIdExpression(appIdExpression: string, templateIdExpression: string): string {
return `${appIdExpression}+'-'+${codeGenToString(templateIdExpression)}`;
}

View File

@ -3,10 +3,10 @@ import {BaseException} from 'angular2/src/facade/exceptions';
import {ListWrapper, SetWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, SetWrapper} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import { import {
CompiledTemplate, CompiledComponentTemplate,
TemplateCmd, TemplateCmd,
nextTemplateId, CompiledHostTemplate,
CompiledHostTemplate BeginComponentCmd
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import { import {
createHostComponentMeta, createHostComponentMeta,
@ -23,7 +23,6 @@ import {CommandCompiler} from './command_compiler';
import {TemplateParser} from './template_parser'; import {TemplateParser} from './template_parser';
import {TemplateNormalizer} from './template_normalizer'; import {TemplateNormalizer} from './template_normalizer';
import {RuntimeMetadataResolver} from './runtime_metadata'; import {RuntimeMetadataResolver} from './runtime_metadata';
import {APP_ID} from 'angular2/src/core/application_tokens';
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler'; import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
import { import {
@ -32,22 +31,19 @@ import {
codeGenValueFn, codeGenValueFn,
MODULE_SUFFIX MODULE_SUFFIX
} from './util'; } from './util';
import {Inject} from 'angular2/src/core/di';
@Injectable() @Injectable()
export class TemplateCompiler { export class TemplateCompiler {
private _hostCacheKeys = new Map<Type, any>(); private _hostCacheKeys = new Map<Type, any>();
private _compiledTemplateCache = new Map<any, CompiledTemplate>(); private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>(); private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
private _appId: string; private _nextTemplateId: number = 0;
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver, constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
private _templateNormalizer: TemplateNormalizer, private _templateNormalizer: TemplateNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _commandCompiler: CommandCompiler, private _commandCompiler: CommandCompiler,
private _cdCompiler: ChangeDetectionCompiler, @Inject(APP_ID) appId: string) { private _cdCompiler: ChangeDetectionCompiler) {}
this._appId = appId;
}
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata): normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise<CompileDirectiveMetadata> { Promise<CompileDirectiveMetadata> {
@ -87,7 +83,7 @@ export class TemplateCompiler {
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set()); this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
} }
return this._compiledTemplateDone.get(hostCacheKey) return this._compiledTemplateDone.get(hostCacheKey)
.then(compiledTemplate => new CompiledHostTemplate(() => compiledTemplate)); .then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
} }
clearCache() { clearCache() {
@ -97,57 +93,55 @@ export class TemplateCompiler {
this._compiledTemplateDone.clear(); this._compiledTemplateDone.clear();
} }
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata, private _compileComponentRuntime(
viewDirectives: CompileDirectiveMetadata[], cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
compilingComponentCacheKeys: Set<any>): CompiledTemplate { compilingComponentCacheKeys: Set<any>): CompiledComponentTemplate {
var compiledTemplate = this._compiledTemplateCache.get(cacheKey); var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
var done = this._compiledTemplateDone.get(cacheKey); var done = this._compiledTemplateDone.get(cacheKey);
if (isBlank(compiledTemplate)) { if (isBlank(compiledTemplate)) {
var styles; var styles = [];
var changeDetectorFactory; var changeDetectorFactory;
var commands; var commands = [];
var templateId = nextTemplateId(); var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
compiledTemplate = compiledTemplate = new CompiledComponentTemplate(
new CompiledTemplate(templateId, (_a, _b) => [changeDetectorFactory, commands, styles]); templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
this._compiledTemplateCache.set(cacheKey, compiledTemplate); this._compiledTemplateCache.set(cacheKey, compiledTemplate);
compilingComponentCacheKeys.add(cacheKey); compilingComponentCacheKeys.add(cacheKey);
done = done = PromiseWrapper
PromiseWrapper.all([ .all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
<any>this._styleCompiler.compileComponentRuntime(this._appId, templateId, viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
compMeta.template) .then((stylesAndNormalizedViewDirMetas: any[]) => {
].concat(viewDirectives.map(dirMeta => var childPromises = [];
this.normalizeDirectiveMetadata(dirMeta)))) var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
.then((stylesAndNormalizedViewDirMetas: any[]) => { var parsedTemplate = this._templateParser.parse(
var childPromises = []; compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
var parsedTemplate = this._templateParser.parse(
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime( var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
compMeta.type, compMeta.changeDetection, parsedTemplate); compMeta.type, compMeta.changeDetection, parsedTemplate);
changeDetectorFactory = changeDetectorFactories[0]; changeDetectorFactory = changeDetectorFactories[0];
styles = stylesAndNormalizedViewDirMetas[0]; var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
commands = this._compileCommandsRuntime(compMeta, templateId, parsedTemplate, tmpStyles.forEach(style => styles.push(style));
changeDetectorFactories, var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
compilingComponentCacheKeys, childPromises); compMeta, parsedTemplate, changeDetectorFactories,
return PromiseWrapper.all(childPromises); compilingComponentCacheKeys, childPromises);
}) tmpCommands.forEach(cmd => commands.push(cmd));
.then((_) => { return PromiseWrapper.all(childPromises);
SetWrapper.delete(compilingComponentCacheKeys, cacheKey); })
return compiledTemplate; .then((_) => {
}); SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
return compiledTemplate;
});
this._compiledTemplateDone.set(cacheKey, done); this._compiledTemplateDone.set(cacheKey, done);
} }
return compiledTemplate; return compiledTemplate;
} }
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, templateId: number, private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
parsedTemplate: TemplateAst[],
changeDetectorFactories: Function[], changeDetectorFactories: Function[],
compilingComponentCacheKeys: Set<Type>, compilingComponentCacheKeys: Set<Type>,
childPromises: Promise<any>[]): TemplateCmd[] { childPromises: Promise<any>[]): TemplateCmd[] {
return this._commandCompiler.compileComponentRuntime( var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
compMeta, this._appId, templateId, parsedTemplate, changeDetectorFactories, compMeta, parsedTemplate, changeDetectorFactories,
(childComponentDir: CompileDirectiveMetadata) => { (childComponentDir: CompileDirectiveMetadata) => {
var childCacheKey = childComponentDir.type.runtime; var childCacheKey = childComponentDir.type.runtime;
var childViewDirectives: CompileDirectiveMetadata[] = var childViewDirectives: CompileDirectiveMetadata[] =
@ -160,8 +154,14 @@ export class TemplateCompiler {
// Only wait for a child if it is not a cycle // Only wait for a child if it is not a cycle
childPromises.push(this._compiledTemplateDone.get(childCacheKey)); childPromises.push(this._compiledTemplateDone.get(childCacheKey));
} }
return childTemplate; return () => childTemplate;
}); });
cmds.forEach(cmd => {
if (cmd instanceof BeginComponentCmd) {
cmd.templateGetter();
}
});
return cmds;
} }
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule { compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
@ -171,40 +171,35 @@ export class TemplateCompiler {
var declarations = []; var declarations = [];
var templateArguments = []; var templateArguments = [];
var componentMetas: CompileDirectiveMetadata[] = []; var componentMetas: CompileDirectiveMetadata[] = [];
var templateIdVariable = 'templateId';
var appIdVariable = 'appId';
components.forEach(componentWithDirs => { components.forEach(componentWithDirs => {
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component; var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
assertComponent(compMeta); assertComponent(compMeta);
componentMetas.push(compMeta); componentMetas.push(compMeta);
this._processTemplateCodeGen(compMeta, appIdVariable, templateIdVariable, this._processTemplateCodeGen(compMeta,
<CompileDirectiveMetadata[]>componentWithDirs.directives, <CompileDirectiveMetadata[]>componentWithDirs.directives,
declarations, templateArguments); declarations, templateArguments);
if (compMeta.dynamicLoadable) { if (compMeta.dynamicLoadable) {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
componentMetas.push(hostMeta); componentMetas.push(hostMeta);
this._processTemplateCodeGen(hostMeta, appIdVariable, templateIdVariable, [compMeta], this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
declarations, templateArguments);
} }
}); });
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata, ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
index: number) => { index: number) => {
var templateDataFn = codeGenValueFn([appIdVariable, templateIdVariable], var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
`[${(<any[]>templateArguments[index]).join(',')}]`); var constructionKeyword = IS_DART ? 'const' : 'new';
var compiledTemplateExpr = var compiledTemplateExpr =
`new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${TEMPLATE_COMMANDS_MODULE_REF}nextTemplateId(),${templateDataFn})`; `${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledComponentTemplate('${templateId}',${(<any[]>templateArguments[index]).join(',')})`;
var variableValueExpr; var variableValueExpr;
if (compMeta.type.isHost) { if (compMeta.type.isHost) {
var factoryName = `_hostTemplateFactory${index}`;
declarations.push(`${codeGenValueFn([], compiledTemplateExpr, factoryName)};`);
var constructionKeyword = IS_DART ? 'const' : 'new';
variableValueExpr = variableValueExpr =
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${factoryName})`; `${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
} else { } else {
variableValueExpr = compiledTemplateExpr; variableValueExpr = compiledTemplateExpr;
} }
declarations.push( var varName = templateVariableName(compMeta.type);
`${codeGenExportVariable(templateVariableName(compMeta.type), compMeta.type.isHost)}${variableValueExpr};`); declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
}); });
var moduleUrl = components[0].component.type.moduleUrl; var moduleUrl = components[0].component.type.moduleUrl;
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n')); return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
@ -214,17 +209,16 @@ export class TemplateCompiler {
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText); return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
} }
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, appIdExpr: string, private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
templateIdExpr: string, directives: CompileDirectiveMetadata[], directives: CompileDirectiveMetadata[],
targetDeclarations: string[], targetTemplateArguments: any[][]) { targetDeclarations: string[], targetTemplateArguments: any[][]) {
var styleExpr = var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
this._styleCompiler.compileComponentCodeGen(appIdExpr, templateIdExpr, compMeta.template);
var parsedTemplate = var parsedTemplate =
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name); this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen( var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
compMeta.type, compMeta.changeDetection, parsedTemplate); compMeta.type, compMeta.changeDetection, parsedTemplate);
var commandsExpr = this._commandCompiler.compileComponentCodeGen( var commandsExpr = this._commandCompiler.compileComponentCodeGen(
compMeta, appIdExpr, templateIdExpr, parsedTemplate, changeDetectorsExprs.expressions, compMeta, parsedTemplate, changeDetectorsExprs.expressions,
codeGenComponentTemplateFactory); codeGenComponentTemplateFactory);
addAll(styleExpr.declarations, targetDeclarations); addAll(styleExpr.declarations, targetDeclarations);
@ -251,6 +245,10 @@ function templateVariableName(type: CompileTypeMetadata): string {
return `${type.name}Template`; return `${type.name}Template`;
} }
function templateGetterName(type: CompileTypeMetadata): string {
return `${templateVariableName(type)}Getter`;
}
function templateModuleUrl(moduleUrl: string): string { function templateModuleUrl(moduleUrl: string): string {
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length); var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`; return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
@ -263,5 +261,5 @@ function addAll(source: any[], target: any[]) {
} }
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string { function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateVariableName(nestedCompType.type)}`; return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
} }

View File

@ -43,31 +43,19 @@ function escapeString(input: string, re: RegExp): string {
}); });
} }
export function codeGenExportVariable(name: string, isConst: boolean = false): string { export function codeGenExportVariable(name: string): string {
if (IS_DART) { if (IS_DART) {
return isConst ? `const ${name} = ` : `final ${name} = `; return `const ${name} = `;
} else { } else {
return `var ${name} = exports['${name}'] = `; return `var ${name} = exports['${name}'] = `;
} }
} }
export function codeGenConcatArray(expression: string): string { export function codeGenConstConstructorCall(name: string): string {
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
}
export function codeGenMapArray(argNames: string[], callback: string): string {
if (IS_DART) { if (IS_DART) {
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`; return `const ${name}`;
} else { } else {
return `.map(function(${argNames.join(',')}) { return ${callback}; })`; return `new ${name}`;
}
}
export function codeGenReplaceAll(pattern: string, expression: string): string {
if (IS_DART) {
return `.replaceAll('${pattern}', ${expression})`;
} else {
return `.replace(/${pattern}/g, ${expression})`;
} }
} }

View File

@ -1,6 +1,6 @@
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang'; import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
import {RenderProtoViewRef} from 'angular2/src/core/render/api'; import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di'; import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
@ -13,12 +13,12 @@ import {ProtoElementInjector, DirectiveProvider} from './element_injector';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {ViewResolver} from './view_resolver'; import {ViewResolver} from './view_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
import {ViewMetadata} from '../metadata/view'; import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
import {AMBIENT_PIPES} from 'angular2/src/core/ambient'; import {AMBIENT_PIPES} from 'angular2/src/core/ambient';
import { import {
visitAllCommands, visitAllCommands,
CompiledTemplate, CompiledComponentTemplate,
CompiledHostTemplate, CompiledHostTemplate,
TemplateCmd, TemplateCmd,
CommandVisitor, CommandVisitor,
@ -36,7 +36,9 @@ import {APP_ID} from 'angular2/src/core/application_tokens';
@Injectable() @Injectable()
export class ProtoViewFactory { export class ProtoViewFactory {
private _cache: Map<number, AppProtoView> = new Map<number, AppProtoView>(); private _cache: Map<string, AppProtoView> = new Map<string, AppProtoView>();
private _nextTemplateId: number = 0;
constructor(private _renderer: Renderer, constructor(private _renderer: Renderer,
@Optional() @Inject(AMBIENT_PIPES) private _ambientPipes: Array<Type | any[]>, @Optional() @Inject(AMBIENT_PIPES) private _ambientPipes: Array<Type | any[]>,
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver, private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
@ -45,13 +47,16 @@ export class ProtoViewFactory {
clearCache() { this._cache.clear(); } clearCache() { this._cache.clear(); }
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView { createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
var compiledTemplate = compiledHostTemplate.getTemplate(); var compiledTemplate = compiledHostTemplate.template;
var result = this._cache.get(compiledTemplate.id); var result = this._cache.get(compiledTemplate.id);
if (isBlank(result)) { if (isBlank(result)) {
var templateData = compiledTemplate.getData(this._appId);
var emptyMap: {[key: string]: PipeProvider} = {}; var emptyMap: {[key: string]: PipeProvider} = {};
result = new AppProtoView(templateData.commands, ViewType.HOST, true, var shortId = `${this._appId}-${this._nextTemplateId++}`;
templateData.changeDetectorFactory, null, new ProtoPipes(emptyMap)); this._renderer.registerComponentTemplate(new RenderComponentTemplate(
compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
result =
new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
this._cache.set(compiledTemplate.id, result); this._cache.set(compiledTemplate.id, result);
} }
return result; return result;
@ -62,18 +67,19 @@ export class ProtoViewFactory {
if (isBlank(nestedProtoView)) { if (isBlank(nestedProtoView)) {
var component = cmd.directives[0]; var component = cmd.directives[0];
var view = this._viewResolver.resolve(component); var view = this._viewResolver.resolve(component);
var compiledTemplateData = cmd.template.getData(this._appId); var compiledTemplate = cmd.templateGetter();
var styles = _flattenStyleArr(compiledTemplate.styles, []);
this._renderer.registerComponentTemplate(cmd.templateId, compiledTemplateData.commands, var shortId = `${this._appId}-${this._nextTemplateId++}`;
compiledTemplateData.styles, cmd.nativeShadow); this._renderer.registerComponentTemplate(new RenderComponentTemplate(
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe)); var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
nestedProtoView = new AppProtoView(compiledTemplateData.commands, ViewType.COMPONENT, true, nestedProtoView = new AppProtoView(
compiledTemplateData.changeDetectorFactory, null, compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
ProtoPipes.fromProviders(boundPipes)); compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
// Note: The cache is updated before recursing // Note: The cache is updated before recursing
// to be able to resolve cycles // to be able to resolve cycles
this._cache.set(cmd.template.id, nestedProtoView); this._cache.set(compiledTemplate.id, nestedProtoView);
this._initializeProtoView(nestedProtoView, null); this._initializeProtoView(nestedProtoView, null);
} }
return nestedProtoView; return nestedProtoView;
@ -81,7 +87,7 @@ export class ProtoViewFactory {
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView { private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
var nestedProtoView = new AppProtoView( var nestedProtoView = new AppProtoView(
cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory, parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config)); arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
if (cmd.isMerged) { if (cmd.isMerged) {
this.initializeProtoViewIfNeeded(nestedProtoView); this.initializeProtoViewIfNeeded(nestedProtoView);
@ -91,7 +97,7 @@ export class ProtoViewFactory {
initializeProtoViewIfNeeded(protoView: AppProtoView) { initializeProtoViewIfNeeded(protoView: AppProtoView) {
if (!protoView.isInitialized()) { if (!protoView.isInitialized()) {
var render = this._renderer.createProtoView(protoView.templateCmds); var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
this._initializeProtoView(protoView, render); this._initializeProtoView(protoView, render);
} }
} }
@ -321,3 +327,15 @@ function _flattenArray(tree: any[], out: Array<Type | Provider | any[]>): void {
} }
} }
} }
function _flattenStyleArr(arr: Array<string | any[]>, out: string[]): string[] {
for (var i = 0; i < arr.length; i++) {
var entry = arr[i];
if (isArray(entry)) {
_flattenStyleArr(<any[]>entry, out);
} else {
out.push(<string>entry);
}
}
return out;
}

View File

@ -1,4 +1,5 @@
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang'; import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import { import {
RenderTemplateCmd, RenderTemplateCmd,
RenderCommandVisitor, RenderCommandVisitor,
@ -8,12 +9,10 @@ import {
RenderBeginComponentCmd, RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/render'; } from 'angular2/src/core/render/render';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
var _nextTemplateId: number = 0; // Export ViewEncapsulation so that compiled templates only need to depend
// on template_commands.
export function nextTemplateId(): number { export {ViewEncapsulation} from 'angular2/src/core/metadata';
return _nextTemplateId++;
}
/** /**
* A compiled host template. * A compiled host template.
@ -23,34 +22,16 @@ export function nextTemplateId(): number {
*/ */
@CONST() @CONST()
export class CompiledHostTemplate { export class CompiledHostTemplate {
// Note: _templateGetter is a function so that CompiledHostTemplate can be constructor(public template: CompiledComponentTemplate) {}
// a const!
constructor(private _templateGetter: Function) {}
getTemplate(): CompiledTemplate { return this._templateGetter(); }
} }
/** /**
* A compiled template. * A compiled template.
*/ */
export class CompiledTemplate { @CONST()
// Note: paramGetter is a function so that we can have cycles between templates! export class CompiledComponentTemplate {
// paramGetter returns a tuple with: constructor(public id: string, public changeDetectorFactory: Function,
// - ChangeDetector factory function public commands: TemplateCmd[], public styles: string[]) {}
// - TemplateCmd[]
// - styles
constructor(public id: number,
private _dataGetter: /*()=>Array<Function, TemplateCmd[], string[]>*/ Function) {}
getData(appId: string): CompiledTemplateData {
var data = this._dataGetter(appId, this.id);
return new CompiledTemplateData(data[0], data[1], data[2]);
}
}
export class CompiledTemplateData {
constructor(public changeDetectorFactory: Function, public commands: TemplateCmd[],
public styles: string[]) {}
} }
const EMPTY_ARR = CONST_EXPR([]); const EMPTY_ARR = CONST_EXPR([]);
@ -59,6 +40,7 @@ export interface TemplateCmd extends RenderTemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any; visit(visitor: RenderCommandVisitor, context: any): any;
} }
@CONST()
export class TextCmd implements TemplateCmd, RenderTextCmd { export class TextCmd implements TemplateCmd, RenderTextCmd {
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {} constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
visit(visitor: RenderCommandVisitor, context: any): any { visit(visitor: RenderCommandVisitor, context: any): any {
@ -66,10 +48,7 @@ export class TextCmd implements TemplateCmd, RenderTextCmd {
} }
} }
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd { @CONST()
return new TextCmd(value, isBound, ngContentIndex);
}
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd { export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
isBound: boolean = false; isBound: boolean = false;
constructor(public index: number, public ngContentIndex: number) {} constructor(public index: number, public ngContentIndex: number) {}
@ -78,17 +57,14 @@ export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
} }
} }
export function ngContent(index: number, ngContentIndex: number): NgContentCmd { export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
return new NgContentCmd(index, ngContentIndex); get variableNameAndValues(): Array<string | number> { return unimplemented(); }
} get eventTargetAndNames(): string[] { return unimplemented(); }
get directives(): Type[] { return unimplemented(); }
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd { abstract visit(visitor: RenderCommandVisitor, context: any): any;
variableNameAndValues: Array<string | number>;
eventTargetAndNames: string[];
directives: Type[];
visit(visitor: RenderCommandVisitor, context: any): any;
} }
@CONST()
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd { export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
constructor(public name: string, public attrNameAndValues: string[], constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[], public eventTargetAndNames: string[],
@ -99,57 +75,40 @@ export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeg
} }
} }
export function beginElement(name: string, attrNameAndValues: string[],
eventTargetAndNames: string[],
variableNameAndValues: Array<string | number>, directives: Type[],
isBound: boolean, ngContentIndex: number): BeginElementCmd {
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
directives, isBound, ngContentIndex);
}
@CONST()
export class EndElementCmd implements TemplateCmd { export class EndElementCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any { visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndElement(context); return visitor.visitEndElement(context);
} }
} }
export function endElement(): TemplateCmd { @CONST()
return new EndElementCmd();
}
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd { export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
isBound: boolean = true; isBound: boolean = true;
templateId: number;
constructor(public name: string, public attrNameAndValues: string[], constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[], public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[], public variableNameAndValues: Array<string | number>, public directives: Type[],
public nativeShadow: boolean, public ngContentIndex: number, public encapsulation: ViewEncapsulation, public ngContentIndex: number,
public template: CompiledTemplate) { // Note: the template needs to be stored as a function
this.templateId = template.id; // so that we can resolve cycles
} public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
get templateId(): string { return this.templateGetter().id; }
visit(visitor: RenderCommandVisitor, context: any): any { visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context); return visitor.visitBeginComponent(this, context);
} }
} }
export function beginComponent( @CONST()
name: string, attrNameAnsValues: string[], eventTargetAndNames: string[],
variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean,
ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd {
return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues,
directives, nativeShadow, ngContentIndex, template);
}
export class EndComponentCmd implements TemplateCmd { export class EndComponentCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any { visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndComponent(context); return visitor.visitEndComponent(context);
} }
} }
export function endComponent(): TemplateCmd { @CONST()
return new EndComponentCmd();
}
export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd, export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
RenderEmbeddedTemplateCmd { RenderEmbeddedTemplateCmd {
isBound: boolean = true; isBound: boolean = true;
@ -163,13 +122,6 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
} }
} }
export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
directives: Type[], isMerged: boolean, ngContentIndex: number,
changeDetectorFactory: Function,
children: TemplateCmd[]): EmbeddedTemplateCmd {
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
ngContentIndex, changeDetectorFactory, children);
}
export interface CommandVisitor extends RenderCommandVisitor { export interface CommandVisitor extends RenderCommandVisitor {
visitText(cmd: TextCmd, context: any): any; visitText(cmd: TextCmd, context: any): any;

View File

@ -318,8 +318,8 @@ export class AppProtoView {
textBindingCount = null; textBindingCount = null;
render: renderApi.RenderProtoViewRef = null; render: renderApi.RenderProtoViewRef = null;
constructor(public templateCmds: TemplateCmd[], public type: ViewType, public isMergable: boolean, constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
public changeDetectorFactory: Function, public isMergable: boolean, public changeDetectorFactory: Function,
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) { public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
this.ref = new ProtoViewRef_(this); this.ref = new ProtoViewRef_(this);
} }

View File

@ -1,4 +1,6 @@
import {unimplemented} from 'angular2/src/facade/exceptions';
import {Map} from 'angular2/src/facade/collection'; import {Map} from 'angular2/src/facade/collection';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
/** /**
* Represents an Angular ProtoView in the Rendering Context. * Represents an Angular ProtoView in the Rendering Context.
@ -69,37 +71,40 @@ export class RenderFragmentRef {}
// TODO(i): refactor into an interface // TODO(i): refactor into an interface
export class RenderViewRef {} export class RenderViewRef {}
export interface RenderTemplateCmd { visit(visitor: RenderCommandVisitor, context: any): any; } export abstract class RenderTemplateCmd {
abstract visit(visitor: RenderCommandVisitor, context: any): any;
export interface RenderBeginCmd extends RenderTemplateCmd {
ngContentIndex: number;
isBound: boolean;
} }
export interface RenderTextCmd extends RenderBeginCmd { value: string; } export abstract class RenderBeginCmd extends RenderTemplateCmd {
get ngContentIndex(): number { return unimplemented(); };
get isBound(): boolean { return unimplemented(); };
}
export interface RenderNgContentCmd { export abstract class RenderTextCmd extends RenderBeginCmd {
get value(): string { return unimplemented(); };
}
export abstract class RenderNgContentCmd extends RenderTemplateCmd {
// The index of this NgContent element // The index of this NgContent element
index: number; get index(): number { return unimplemented(); };
// The index of the NgContent element into which this // The index of the NgContent element into which this
// NgContent element should be projected (if any) // NgContent element should be projected (if any)
ngContentIndex: number; get ngContentIndex(): number { return unimplemented(); };
} }
export interface RenderBeginElementCmd extends RenderBeginCmd { export abstract class RenderBeginElementCmd extends RenderBeginCmd {
name: string; get name(): string { return unimplemented(); };
attrNameAndValues: string[]; get attrNameAndValues(): string[] { return unimplemented(); };
eventTargetAndNames: string[]; get eventTargetAndNames(): string[] { return unimplemented(); };
} }
export interface RenderBeginComponentCmd extends RenderBeginElementCmd { export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
nativeShadow: boolean; get templateId(): string { return unimplemented(); };
templateId: number;
} }
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd { export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
isMerged: boolean; get isMerged(): boolean { return unimplemented(); };
children: RenderTemplateCmd[]; get children(): RenderTemplateCmd[] { return unimplemented(); };
} }
export interface RenderCommandVisitor { export interface RenderCommandVisitor {
@ -156,6 +161,10 @@ export interface RenderElementRef {
boundElementIndex: number; boundElementIndex: number;
} }
export class RenderComponentTemplate {
constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
public commands: RenderTemplateCmd[], public styles: string[]) {}
}
/** /**
* Injectable service that provides a low-level interface for modifying the UI. * Injectable service that provides a low-level interface for modifying the UI.
@ -177,13 +186,13 @@ export abstract class Renderer {
* Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when * Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
* {@link #createProtoView creating Render ProtoView}. * {@link #createProtoView creating Render ProtoView}.
*/ */
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], abstract registerComponentTemplate(template: RenderComponentTemplate);
styles: string[], nativeShadow: boolean);
/** /**
* Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s. * Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
*/ */
abstract createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef; abstract createProtoView(componentTemplateId: string,
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
/** /**
* Creates a Root Host View based on the provided `hostProtoViewRef`. * Creates a Root Host View based on the provided `hostProtoViewRef`.

View File

@ -1,6 +1,13 @@
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di'; import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder'; import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {isPresent, isBlank, RegExpWrapper, CONST_EXPR, stringify} from 'angular2/src/facade/lang'; import {
isPresent,
isBlank,
RegExpWrapper,
CONST_EXPR,
stringify,
StringWrapper
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions'; import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {DOM} from 'angular2/src/core/dom/dom_adapter';
@ -18,13 +25,15 @@ import {
RenderFragmentRef, RenderFragmentRef,
RenderViewWithFragments, RenderViewWithFragments,
RenderTemplateCmd, RenderTemplateCmd,
RenderEventDispatcher RenderEventDispatcher,
RenderComponentTemplate
} from '../api'; } from '../api';
import {DOCUMENT} from './dom_tokens'; import {DOCUMENT} from './dom_tokens';
import {createRenderView, NodeFactory} from '../view_factory'; import {createRenderView, NodeFactory, encapsulateStyles} from '../view_factory';
import {DefaultRenderView, DefaultRenderFragmentRef, DefaultProtoViewRef} from '../view'; import {DefaultRenderView, DefaultRenderFragmentRef, DefaultProtoViewRef} from '../view';
import {camelCaseToDashCase} from './util'; import {camelCaseToDashCase} from './util';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
// TODO(tbosch): solve SVG properly once https://github.com/angular/angular/issues/4417 is done // TODO(tbosch): solve SVG properly once https://github.com/angular/angular/issues/4417 is done
const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'; const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
@ -113,14 +122,12 @@ const SVG_ELEMENT_NAMES = CONST_EXPR({
const SVG_ATTR_NAMESPACES = CONST_EXPR({'href': XLINK_NAMESPACE, 'xlink:href': XLINK_NAMESPACE}); const SVG_ATTR_NAMESPACES = CONST_EXPR({'href': XLINK_NAMESPACE, 'xlink:href': XLINK_NAMESPACE});
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> { export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], abstract registerComponentTemplate(template: RenderComponentTemplate);
styles: string[], nativeShadow: boolean);
abstract resolveComponentTemplate(templateId: number): RenderTemplateCmd[]; abstract resolveComponentTemplate(templateId: string): RenderComponentTemplate;
createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef { abstract createProtoView(componentTemplateId: string,
return new DefaultProtoViewRef(cmds); cmds: RenderTemplateCmd[]): RenderProtoViewRef;
}
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number, abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
hostElementSelector: string): RenderViewWithFragments; hostElementSelector: string): RenderViewWithFragments;
@ -189,7 +196,7 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
} }
abstract createElement(name: string, attrNameAndValues: string[]): Node; abstract createElement(name: string, attrNameAndValues: string[]): Node;
abstract mergeElement(existing: Node, attrNameAndValues: string[]); abstract mergeElement(existing: Node, attrNameAndValues: string[]);
abstract createShadowRoot(host: Node, templateId: number): Node; abstract createShadowRoot(host: Node, templateId: string): Node;
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); } createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); } appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
abstract on(element: Node, eventName: string, callback: Function); abstract on(element: Node, eventName: string, callback: Function);
@ -252,8 +259,8 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
@Injectable() @Injectable()
export class DomRenderer_ extends DomRenderer { export class DomRenderer_ extends DomRenderer {
private _componentCmds: Map<number, RenderTemplateCmd[]> = new Map<number, RenderTemplateCmd[]>(); private _componentTpls: Map<string, RenderComponentTemplate> =
private _nativeShadowStyles: Map<number, string[]> = new Map<number, string[]>(); new Map<string, RenderComponentTemplate>();
private _document; private _document;
constructor(private _eventManager: EventManager, constructor(private _eventManager: EventManager,
@ -263,18 +270,20 @@ export class DomRenderer_ extends DomRenderer {
this._document = document; this._document = document;
} }
registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[], registerComponentTemplate(template: RenderComponentTemplate) {
nativeShadow: boolean) { this._componentTpls.set(template.id, template);
this._componentCmds.set(templateId, commands); if (template.encapsulation !== ViewEncapsulation.Native) {
if (nativeShadow) { var encapsulatedStyles = encapsulateStyles(template);
this._nativeShadowStyles.set(templateId, styles); this._domSharedStylesHost.addStyles(encapsulatedStyles);
} else {
this._domSharedStylesHost.addStyles(styles);
} }
} }
resolveComponentTemplate(templateId: number): RenderTemplateCmd[] { createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
return this._componentCmds.get(templateId); return new DefaultProtoViewRef(this._componentTpls.get(componentTemplateId), cmds);
}
resolveComponentTemplate(templateId: string): RenderComponentTemplate {
return this._componentTpls.get(templateId);
} }
/** @internal */ /** @internal */
@ -299,7 +308,8 @@ export class DomRenderer_ extends DomRenderer {
private _createView(protoViewRef: RenderProtoViewRef, private _createView(protoViewRef: RenderProtoViewRef,
inplaceElement: HTMLElement): RenderViewWithFragments { inplaceElement: HTMLElement): RenderViewWithFragments {
var view = createRenderView((<DefaultProtoViewRef>protoViewRef).cmds, inplaceElement, this); var dpvr = <DefaultProtoViewRef>protoViewRef;
var view = createRenderView(dpvr.template, dpvr.cmds, inplaceElement, this);
var sdRoots = view.nativeShadowRoots; var sdRoots = view.nativeShadowRoots;
for (var i = 0; i < sdRoots.length; i++) { for (var i = 0; i < sdRoots.length; i++) {
this._domSharedStylesHost.addHost(sdRoots[i]); this._domSharedStylesHost.addHost(sdRoots[i]);
@ -375,11 +385,11 @@ export class DomRenderer_ extends DomRenderer {
createRootContentInsertionPoint(): Node { createRootContentInsertionPoint(): Node {
return DOM.createComment('root-content-insertion-point'); return DOM.createComment('root-content-insertion-point');
} }
createShadowRoot(host: Node, templateId: number): Node { createShadowRoot(host: Node, templateId: string): Node {
var sr = DOM.createShadowRoot(host); var sr = DOM.createShadowRoot(host);
var styles = this._nativeShadowStyles.get(templateId); var tpl = this._componentTpls.get(templateId);
for (var i = 0; i < styles.length; i++) { for (var i = 0; i < tpl.styles.length; i++) {
DOM.appendChild(sr, DOM.createStyleElement(styles[i])); DOM.appendChild(sr, DOM.createStyleElement(tpl.styles[i]));
} }
return sr; return sr;
} }

View File

@ -3,6 +3,7 @@ import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facad
import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang'; import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
import { import {
RenderComponentTemplate,
RenderViewRef, RenderViewRef,
RenderEventDispatcher, RenderEventDispatcher,
RenderTemplateCmd, RenderTemplateCmd,
@ -11,7 +12,9 @@ import {
} from './api'; } from './api';
export class DefaultProtoViewRef extends RenderProtoViewRef { export class DefaultProtoViewRef extends RenderProtoViewRef {
constructor(public cmds: RenderTemplateCmd[]) { super(); } constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
super();
}
} }
export class DefaultRenderFragmentRef<N> extends RenderFragmentRef { export class DefaultRenderFragmentRef<N> extends RenderFragmentRef {

View File

@ -1,4 +1,4 @@
import {isBlank, isPresent} from 'angular2/src/facade/lang'; import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
import { import {
RenderEventDispatcher, RenderEventDispatcher,
RenderTemplateCmd, RenderTemplateCmd,
@ -7,17 +7,34 @@ import {
RenderBeginComponentCmd, RenderBeginComponentCmd,
RenderNgContentCmd, RenderNgContentCmd,
RenderTextCmd, RenderTextCmd,
RenderEmbeddedTemplateCmd RenderEmbeddedTemplateCmd,
RenderComponentTemplate
} from './api'; } from './api';
import {DefaultRenderView, DefaultRenderFragmentRef} from './view'; import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
import {ListWrapper} from 'angular2/src/facade/collection';
export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceElement: any,
export function encapsulateStyles(componentTemplate: RenderComponentTemplate): string[] {
var processedStyles = componentTemplate.styles;
if (componentTemplate.encapsulation === ViewEncapsulation.Emulated) {
processedStyles = ListWrapper.createFixedSize(componentTemplate.styles.length);
for (var i = 0; i < componentTemplate.styles.length; i++) {
processedStyles[i] = StringWrapper.replaceAll(componentTemplate.styles[i], COMPONENT_REGEX,
componentTemplate.shortId);
}
}
return processedStyles;
}
export function createRenderView(componentTemplate: RenderComponentTemplate,
cmds: RenderTemplateCmd[], inplaceElement: any,
nodeFactory: NodeFactory<any>): DefaultRenderView<any> { nodeFactory: NodeFactory<any>): DefaultRenderView<any> {
var view: DefaultRenderView<any>; var view: DefaultRenderView<any>;
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) => var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
view.dispatchRenderEvent(boundElementIndex, eventName, event); view.dispatchRenderEvent(boundElementIndex, eventName, event);
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement); var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
context.build(fragmentCmds); context.build(componentTemplate, cmds);
var fragments: DefaultRenderFragmentRef<any>[] = []; var fragments: DefaultRenderFragmentRef<any>[] = [];
for (var i = 0; i < context.fragments.length; i++) { for (var i = 0; i < context.fragments.length; i++) {
fragments.push(new DefaultRenderFragmentRef(context.fragments[i])); fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
@ -29,12 +46,12 @@ export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceEleme
} }
export interface NodeFactory<N> { export interface NodeFactory<N> {
resolveComponentTemplate(templateId: number): RenderTemplateCmd[]; resolveComponentTemplate(templateId: string): RenderComponentTemplate;
createTemplateAnchor(attrNameAndValues: string[]): N; createTemplateAnchor(attrNameAndValues: string[]): N;
createElement(name: string, attrNameAndValues: string[]): N; createElement(name: string, attrNameAndValues: string[]): N;
createRootContentInsertionPoint(): N; createRootContentInsertionPoint(): N;
mergeElement(existing: N, attrNameAndValues: string[]); mergeElement(existing: N, attrNameAndValues: string[]);
createShadowRoot(host: N, templateId: number): N; createShadowRoot(host: N, templateId: string): N;
createText(value: string): N; createText(value: string): N;
appendChild(parent: N, child: N); appendChild(parent: N, child: N);
on(element: N, eventName: string, callback: Function); on(element: N, eventName: string, callback: Function);
@ -57,8 +74,8 @@ class BuildContext<N> {
componentCount: number = 0; componentCount: number = 0;
isHost: boolean; isHost: boolean;
build(fragmentCmds: RenderTemplateCmd[]) { build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
this.enqueueFragmentBuilder(null, fragmentCmds); this.enqueueRootBuilder(template, cmds);
this._build(this._builders[0]); this._build(this._builders[0]);
} }
@ -73,14 +90,22 @@ class BuildContext<N> {
enqueueComponentBuilder(component: Component<N>) { enqueueComponentBuilder(component: Component<N>) {
this.componentCount++; this.componentCount++;
this._builders.push(new RenderViewBuilder<N>( this._builders.push(
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId))); new RenderViewBuilder<N>(component, null, component.template, component.template.commands));
} }
enqueueFragmentBuilder(parentComponent: Component<N>, commands: RenderTemplateCmd[]) { enqueueFragmentBuilder(parentComponent: Component<N>, parentTemplate: RenderComponentTemplate,
commands: RenderTemplateCmd[]) {
var rootNodes = []; var rootNodes = [];
this.fragments.push(rootNodes); this.fragments.push(rootNodes);
this._builders.push(new RenderViewBuilder<N>(parentComponent, rootNodes, commands)); this._builders.push(
new RenderViewBuilder<N>(parentComponent, rootNodes, parentTemplate, commands));
}
enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
var rootNodes = [];
this.fragments.push(rootNodes);
this._builders.push(new RenderViewBuilder<N>(null, rootNodes, template, cmds));
} }
consumeInplaceElement(): N { consumeInplaceElement(): N {
@ -116,14 +141,15 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
parentStack: Array<N | Component<N>>; parentStack: Array<N | Component<N>>;
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[], constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
public commands: RenderTemplateCmd[]) { public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot; var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
this.parentStack = [rootNodesParent]; this.parentStack = [rootNodesParent];
} }
build(context: BuildContext<N>) { build(context: BuildContext<N>) {
for (var i = 0; i < this.commands.length; i++) { var cmds = this.cmds;
this.commands[i].visit(this, context); for (var i = 0; i < cmds.length; i++) {
cmds[i].visit(this, context);
} }
} }
@ -158,7 +184,7 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
return null; return null;
} }
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any { visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
this.parentStack.push(this._beginElement(cmd, context)); this.parentStack.push(this._beginElement(cmd, context, null));
return null; return null;
} }
visitEndElement(context: BuildContext<N>): any { visitEndElement(context: BuildContext<N>): any {
@ -166,14 +192,17 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
return null; return null;
} }
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any { visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any {
var el = this._beginElement(cmd, context); var templateId = cmd.templateId;
var tpl = context.factory.resolveComponentTemplate(templateId);
var el = this._beginElement(cmd, context, tpl);
var root = el; var root = el;
if (cmd.nativeShadow) {
root = context.factory.createShadowRoot(el, cmd.templateId); if (tpl.encapsulation === ViewEncapsulation.Native) {
root = context.factory.createShadowRoot(el, templateId);
context.nativeShadowRoots.push(root); context.nativeShadowRoots.push(root);
} }
var isRoot = context.componentCount === 0 && context.isHost; var isRoot = context.componentCount === 0 && context.isHost;
var component = new Component(el, root, cmd, isRoot); var component = new Component(el, root, isRoot, tpl);
context.enqueueComponentBuilder(component); context.enqueueComponentBuilder(component);
this.parentStack.push(component); this.parentStack.push(component);
return null; return null;
@ -187,18 +216,34 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
this._addChild(el, cmd.ngContentIndex, context); this._addChild(el, cmd.ngContentIndex, context);
context.boundElements.push(el); context.boundElements.push(el);
if (cmd.isMerged) { if (cmd.isMerged) {
context.enqueueFragmentBuilder(this.parentComponent, cmd.children); context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
} }
return null; return null;
} }
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): N { private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>,
componentTemplate: RenderComponentTemplate): N {
var el: N = context.consumeInplaceElement(); var el: N = context.consumeInplaceElement();
var attrNameAndValues = cmd.attrNameAndValues;
if (this.template.encapsulation === ViewEncapsulation.Emulated) {
// Note: Need to clone attrNameAndValues to make it writable!
if (isPresent(componentTemplate)) {
attrNameAndValues = attrNameAndValues.concat([
_shimContentAttribute(this.template.shortId),
'',
_shimHostAttribute(componentTemplate.shortId),
''
]);
} else {
attrNameAndValues =
attrNameAndValues.concat([_shimContentAttribute(this.template.shortId), '']);
}
}
if (isPresent(el)) { if (isPresent(el)) {
context.factory.mergeElement(el, cmd.attrNameAndValues); context.factory.mergeElement(el, attrNameAndValues);
this.fragmentRootNodes.push(el); this.fragmentRootNodes.push(el);
} else { } else {
el = context.factory.createElement(cmd.name, cmd.attrNameAndValues); el = context.factory.createElement(cmd.name, attrNameAndValues);
this._addChild(el, cmd.ngContentIndex, context); this._addChild(el, cmd.ngContentIndex, context);
} }
if (cmd.isBound) { if (cmd.isBound) {
@ -232,11 +277,11 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
class Component<N> { class Component<N> {
private contentNodesByNgContentIndex: N[][] = []; private contentNodesByNgContentIndex: N[][] = [];
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd, constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
public isRoot: boolean) {} public template: RenderComponentTemplate) {}
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) { addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
if (isBlank(ngContentIndex)) { if (isBlank(ngContentIndex)) {
if (this.cmd.nativeShadow) { if (this.template.encapsulation === ViewEncapsulation.Native) {
context.factory.appendChild(this.hostElement, node); context.factory.appendChild(this.hostElement, node);
} }
} else { } else {
@ -252,3 +297,16 @@ class Component<N> {
[]; [];
} }
} }
var COMPONENT_REGEX = /%COMP%/g;
export const COMPONENT_VARIABLE = '%COMP%';
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
function _shimContentAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
}
function _shimHostAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
}

View File

@ -53,7 +53,7 @@ export class WebWorkerEndElementCmd implements RenderTemplateCmd {
export class WebWorkerBeginComponentCmd implements RenderBeginComponentCmd { export class WebWorkerBeginComponentCmd implements RenderBeginComponentCmd {
constructor(public isBound: boolean, public ngContentIndex: number, public name: string, constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
public attrNameAndValues: string[], public eventTargetAndNames: string[], public attrNameAndValues: string[], public eventTargetAndNames: string[],
public nativeShadow: boolean, public templateId: number) {} public templateId: string) {}
visit(visitor: RenderCommandVisitor, context: any): any { visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context); return visitor.visitBeginComponent(this, context);
} }

View File

@ -13,7 +13,8 @@ import {
RenderNgContentCmd, RenderNgContentCmd,
RenderBeginElementCmd, RenderBeginElementCmd,
RenderBeginComponentCmd, RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd RenderEmbeddedTemplateCmd,
RenderComponentTemplate
} from "angular2/src/core/render/api"; } from "angular2/src/core/render/api";
import { import {
WebWorkerElementRef, WebWorkerElementRef,
@ -31,6 +32,7 @@ import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_pr
import { import {
RenderViewWithFragmentsStore RenderViewWithFragmentsStore
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store'; } from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean) // PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
// We set it to String so that it is considered a Type. // We set it to String so that it is considered a Type.
@ -41,7 +43,7 @@ export class Serializer {
constructor(private _protoViewStore: RenderProtoViewRefStore, constructor(private _protoViewStore: RenderProtoViewRefStore,
private _renderViewStore: RenderViewWithFragmentsStore) {} private _renderViewStore: RenderViewWithFragmentsStore) {}
serialize(obj: any, type: Type): Object { serialize(obj: any, type: any): Object {
if (!isPresent(obj)) { if (!isPresent(obj)) {
return null; return null;
} }
@ -61,12 +63,16 @@ export class Serializer {
return this._serializeWorkerElementRef(obj); return this._serializeWorkerElementRef(obj);
} else if (type == WebWorkerTemplateCmd) { } else if (type == WebWorkerTemplateCmd) {
return serializeTemplateCmd(obj); return serializeTemplateCmd(obj);
} else if (type === RenderComponentTemplate) {
return this._serializeRenderTemplate(obj);
} else if (type === ViewEncapsulation) {
return serializeEnum(obj);
} else { } else {
throw new BaseException("No serializer for " + type.toString()); throw new BaseException("No serializer for " + type.toString());
} }
} }
deserialize(map: any, type: Type, data?: any): any { deserialize(map: any, type: any, data?: any): any {
if (!isPresent(map)) { if (!isPresent(map)) {
return null; return null;
} }
@ -89,6 +95,10 @@ export class Serializer {
return this._deserializeWorkerElementRef(map); return this._deserializeWorkerElementRef(map);
} else if (type == WebWorkerTemplateCmd) { } else if (type == WebWorkerTemplateCmd) {
return deserializeTemplateCmd(map); return deserializeTemplateCmd(map);
} else if (type === RenderComponentTemplate) {
return this._deserializeRenderTemplate(map);
} else if (type === ViewEncapsulation) {
return VIEW_ENCAPSULATION_VALUES[map];
} else { } else {
throw new BaseException("No deserializer for " + type.toString()); throw new BaseException("No deserializer for " + type.toString());
} }
@ -137,8 +147,27 @@ export class Serializer {
return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef), return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
map['boundElementIndex']); map['boundElementIndex']);
} }
private _serializeRenderTemplate(obj: RenderComponentTemplate): Object {
return {
'id': obj.id,
'shortId': obj.shortId,
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
'commands': this.serialize(obj.commands, WebWorkerTemplateCmd),
'styles': this.serialize(obj.styles, PRIMITIVE)
};
}
private _deserializeRenderTemplate(map: {[key: string]: any}): RenderComponentTemplate {
return new RenderComponentTemplate(map['id'], map['shortId'],
this.deserialize(map['encapsulation'], ViewEncapsulation),
this.deserialize(map['commands'], WebWorkerTemplateCmd),
this.deserialize(map['styles'], PRIMITIVE));
}
} }
function serializeTemplateCmd(cmd: RenderTemplateCmd): Object { function serializeTemplateCmd(cmd: RenderTemplateCmd): Object {
return cmd.visit(RENDER_TEMPLATE_CMD_SERIALIZER, null); return cmd.visit(RENDER_TEMPLATE_CMD_SERIALIZER, null);
} }
@ -178,7 +207,6 @@ class RenderTemplateCmdSerializer implements RenderCommandVisitor {
'name': cmd.name, 'name': cmd.name,
'attrNameAndValues': cmd.attrNameAndValues, 'attrNameAndValues': cmd.attrNameAndValues,
'eventTargetAndNames': cmd.eventTargetAndNames, 'eventTargetAndNames': cmd.eventTargetAndNames,
'nativeShadow': cmd.nativeShadow,
'templateId': cmd.templateId 'templateId': cmd.templateId
}; };
} }
@ -210,7 +238,7 @@ var RENDER_TEMPLATE_CMD_DESERIALIZERS = [
(data: {[key: string]: any}) => new WebWorkerEndElementCmd(), (data: {[key: string]: any}) => new WebWorkerEndElementCmd(),
(data: {[key: string]: any}) => new WebWorkerBeginComponentCmd( (data: {[key: string]: any}) => new WebWorkerBeginComponentCmd(
data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'], data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],
data['eventTargetAndNames'], data['nativeShadow'], data['templateId']), data['eventTargetAndNames'], data['templateId']),
(data: {[key: string]: any}) => new WebWorkerEndComponentCmd(), (data: {[key: string]: any}) => new WebWorkerEndComponentCmd(),
(data: {[key: string]: any}) => new WebWorkerEmbeddedTemplateCmd( (data: {[key: string]: any}) => new WebWorkerEmbeddedTemplateCmd(
data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'], data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],

View File

@ -6,7 +6,8 @@ import {
RenderFragmentRef, RenderFragmentRef,
RenderProtoViewRef, RenderProtoViewRef,
Renderer, Renderer,
RenderTemplateCmd RenderTemplateCmd,
RenderComponentTemplate
} from 'angular2/src/core/render/api'; } from 'angular2/src/core/render/api';
import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api'; import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api';
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
@ -31,10 +32,9 @@ export class MessageBasedRenderer {
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL); var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
this._bus.initChannel(EVENT_CHANNEL); this._bus.initChannel(EVENT_CHANNEL);
broker.registerMethod("registerComponentTemplate", broker.registerMethod("registerComponentTemplate", [RenderComponentTemplate],
[PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE, PRIMITIVE],
bind(this._renderer.registerComponentTemplate, this._renderer)); bind(this._renderer.registerComponentTemplate, this._renderer));
broker.registerMethod("createProtoView", [WebWorkerTemplateCmd, PRIMITIVE], broker.registerMethod("createProtoView", [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE],
bind(this._createProtoView, this)); bind(this._createProtoView, this));
broker.registerMethod("createRootHostView", broker.registerMethod("createRootHostView",
[RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE], [RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE],
@ -73,8 +73,9 @@ export class MessageBasedRenderer {
this._renderViewWithFragmentsStore.remove(viewRef); this._renderViewWithFragmentsStore.remove(viewRef);
} }
private _createProtoView(cmds: RenderTemplateCmd[], refIndex: number) { private _createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[],
var protoViewRef = this._renderer.createProtoView(cmds); refIndex: number) {
var protoViewRef = this._renderer.createProtoView(componentTemplateId, cmds);
this._renderProtoViewRefStore.store(protoViewRef, refIndex); this._renderProtoViewRefStore.store(protoViewRef, refIndex);
} }

View File

@ -6,7 +6,8 @@ import {
RenderEventDispatcher, RenderEventDispatcher,
RenderViewWithFragments, RenderViewWithFragments,
RenderFragmentRef, RenderFragmentRef,
RenderTemplateCmd RenderTemplateCmd,
RenderComponentTemplate
} from 'angular2/src/core/render/api'; } from 'angular2/src/core/render/api';
import { import {
ClientMessageBroker, ClientMessageBroker,
@ -35,23 +36,20 @@ export class WebWorkerRenderer implements Renderer {
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL); this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
} }
registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[], registerComponentTemplate(template: RenderComponentTemplate) {
nativeShadow: boolean) { var fnArgs = [new FnArg(template, RenderComponentTemplate)];
var fnArgs = [
new FnArg(templateId, null),
new FnArg(commands, WebWorkerTemplateCmd),
new FnArg(styles, null),
new FnArg(nativeShadow, null)
];
var args = new UiArguments("registerComponentTemplate", fnArgs); var args = new UiArguments("registerComponentTemplate", fnArgs);
this._messageBroker.runOnService(args, null); this._messageBroker.runOnService(args, null);
} }
createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef { createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
var renderProtoViewRef = this._renderProtoViewRefStore.allocate(); var renderProtoViewRef = this._renderProtoViewRefStore.allocate();
var fnArgs: FnArg[] = var fnArgs: FnArg[] = [
[new FnArg(cmds, WebWorkerTemplateCmd), new FnArg(renderProtoViewRef, RenderProtoViewRef)]; new FnArg(componentTemplateId, null),
new FnArg(cmds, WebWorkerTemplateCmd),
new FnArg(renderProtoViewRef, RenderProtoViewRef)
];
var args: UiArguments = new UiArguments("createProtoView", fnArgs); var args: UiArguments = new UiArguments("createProtoView", fnArgs);
this._messageBroker.runOnService(args, null); this._messageBroker.runOnService(args, null);
return renderProtoViewRef; return renderProtoViewRef;

View File

@ -134,7 +134,8 @@ function createTestableModule(source: SourceExpressions,
var resultExpression = var resultExpression =
`${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`; `${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`;
var testableSource = `${source.declarations.join('\n')} var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`; ${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(null, testableSource); return new SourceModule(null, testableSource);
} }

View File

@ -13,7 +13,15 @@ import {
beforeEachBindings beforeEachBindings
} from 'angular2/testing_internal'; } from 'angular2/testing_internal';
import {CONST_EXPR, stringify, isType, Type, isBlank} from 'angular2/src/facade/lang'; import {
CONST_EXPR,
stringify,
isType,
Type,
isBlank,
serializeEnum,
IS_DART
} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection'; import {MapWrapper} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {TemplateParser} from 'angular2/src/compiler/template_parser'; import {TemplateParser} from 'angular2/src/compiler/template_parser';
@ -26,7 +34,7 @@ import {
EmbeddedTemplateCmd, EmbeddedTemplateCmd,
TemplateCmd, TemplateCmd,
visitAllCommands, visitAllCommands,
CompiledTemplate CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import {CommandCompiler} from 'angular2/src/compiler/command_compiler'; import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
import { import {
@ -41,6 +49,7 @@ import {
escapeSingleQuoteString, escapeSingleQuoteString,
codeGenValueFn, codeGenValueFn,
codeGenExportVariable, codeGenExportVariable,
codeGenConstConstructorCall,
MODULE_SUFFIX MODULE_SUFFIX
} from 'angular2/src/compiler/util'; } from 'angular2/src/compiler/util';
import {TEST_PROVIDERS} from './test_bindings'; import {TEST_PROVIDERS} from './test_bindings';
@ -70,11 +79,8 @@ var SomeDirTypeMeta =
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL}); new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL});
var ACompTypeMeta = var ACompTypeMeta =
new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleUrl: THIS_MODULE_URL}); new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleUrl: THIS_MODULE_URL});
var compTypeTemplateId: Map<CompileTypeMetadata, number> = var compTypeTemplateId: Map<CompileTypeMetadata, string> = MapWrapper.createFromPairs(
MapWrapper.createFromPairs([[RootCompTypeMeta, 1], [SomeDirTypeMeta, 2], [ACompTypeMeta, 3]]); [[RootCompTypeMeta, 'rootCompId'], [SomeDirTypeMeta, 'someDirId'], [ACompTypeMeta, 'aCompId']]);
const APP_ID = 'app1';
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
export function main() { export function main() {
describe('CommandCompiler', () => { describe('CommandCompiler', () => {
@ -260,22 +266,6 @@ export function main() {
}); });
})); }));
it('should emulate style encapsulation', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div>',
encapsulation: ViewEncapsulation.Emulated
});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', ['_ngcontent-app1-1', ''], [], [], [], false, null],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create nested nodes', inject([AsyncTestCompleter], (async) => { it('should create nested nodes', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<div>a</div>'}); var rootComp = createComp({type: RootCompTypeMeta, template: '<div>a</div>'});
run(rootComp, []) run(rootComp, [])
@ -306,9 +296,9 @@ export function main() {
[null, 'click'], [null, 'click'],
['someVar', 0], ['someVar', 0],
['ACompType'], ['ACompType'],
false, serializeEnum(ViewEncapsulation.None),
null, null,
3 'aCompId'
], ],
[END_COMPONENT] [END_COMPONENT]
]); ]);
@ -316,43 +306,24 @@ export function main() {
}); });
})); }));
it('should emulate style encapsulation on host elements', it('should store viewEncapsulation', inject([AsyncTestCompleter], (async) => {
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<a></a>',
encapsulation: ViewEncapsulation.Emulated
});
var comp = createComp(
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Emulated});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
['_nghost-app1-3', '', '_ngcontent-app1-1', ''],
[],
[],
['ACompType'],
false,
null,
3
],
[END_COMPONENT]
]);
async.done();
});
}));
it('should set nativeShadow flag', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<a></a>'}); var rootComp = createComp({type: RootCompTypeMeta, template: '<a></a>'});
var comp = createComp( var comp = createComp(
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Native}); {type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Native});
run(rootComp, [comp]) run(rootComp, [comp])
.then((data) => { .then((data) => {
expect(data).toEqual([ expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 3], [
BEGIN_COMPONENT,
'a',
[],
[],
[],
['ACompType'],
serializeEnum(ViewEncapsulation.Native),
null,
'aCompId'
],
[END_COMPONENT] [END_COMPONENT]
]); ]);
async.done(); async.done();
@ -366,7 +337,17 @@ export function main() {
run(rootComp, [comp]) run(rootComp, [comp])
.then((data) => { .then((data) => {
expect(data).toEqual([ expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 3], [
BEGIN_COMPONENT,
'a',
[],
[],
[],
['ACompType'],
serializeEnum(ViewEncapsulation.None),
null,
'aCompId'
],
[TEXT, 't', false, 0], [TEXT, 't', false, 0],
[END_COMPONENT] [END_COMPONENT]
]); ]);
@ -472,7 +453,8 @@ export function main() {
describe('compileComponentRuntime', () => { describe('compileComponentRuntime', () => {
beforeEach(() => { beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => { componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return new CompiledTemplate(compTypeTemplateId.get(directive.type), () => []); return () => new CompiledComponentTemplate(compTypeTemplateId.get(directive.type), null,
null, null);
}; };
}); });
@ -485,8 +467,7 @@ export function main() {
var parsedTemplate = var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name); parser.parse(component.template.template, directives, component.type.name);
var commands = commandCompiler.compileComponentRuntime( var commands = commandCompiler.compileComponentRuntime(
component, APP_ID, compTypeTemplateId.get(component.type), parsedTemplate, component, parsedTemplate, changeDetectorFactories, componentTemplateFactory);
changeDetectorFactories, componentTemplateFactory);
return PromiseWrapper.resolve(humanize(commands)); return PromiseWrapper.resolve(humanize(commands));
} }
@ -497,22 +478,34 @@ export function main() {
describe('compileComponentCodeGen', () => { describe('compileComponentCodeGen', () => {
beforeEach(() => { beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => { componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compTypeTemplateId.get(directive.type)}, ${codeGenValueFn([], '{}')})`; return `${directive.type.name}TemplateGetter`;
}; };
}); });
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
embeddedTemplateCount: number = 0): Promise<any[][]> { embeddedTemplateCount: number = 0): Promise<any[][]> {
var testDeclarations = [];
var changeDetectorFactoryExpressions = []; var changeDetectorFactoryExpressions = [];
for (var i = 0; i < embeddedTemplateCount + 1; i++) { for (var i = 0; i < embeddedTemplateCount + 1; i++) {
changeDetectorFactoryExpressions.push(codeGenValueFn(['_'], `'cd${i}'`)); var fnName = `cd${i}`;
testDeclarations.push(`${codeGenValueFn(['_'], ` 'cd${i}' `, fnName)};`);
changeDetectorFactoryExpressions.push(fnName);
}
for (var i = 0; i < directives.length; i++) {
var directive = directives[i];
if (directive.isComponent) {
var nestedTemplate =
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'CompiledComponentTemplate')}('${compTypeTemplateId.get(directive.type)}', null, null, null)`;
var getterName = `${directive.type.name}TemplateGetter`;
testDeclarations.push(`${codeGenValueFn([], nestedTemplate, getterName)};`)
}
} }
var parsedTemplate = var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name); parser.parse(component.template.template, directives, component.type.name);
var sourceModule = commandCompiler.compileComponentCodeGen( var sourceExpression = commandCompiler.compileComponentCodeGen(
component, `'${APP_ID}'`, `${compTypeTemplateId.get(component.type)}`, parsedTemplate, component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory);
changeDetectorFactoryExpressions, componentTemplateFactory); testDeclarations.forEach(decl => sourceExpression.declarations.push(decl));
var testableModule = createTestableModule(sourceModule).getSourceWithImports(); var testableModule = createTestableModule(sourceExpression).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null); return evalModule(testableModule.source, testableModule.imports, null);
} }
@ -569,9 +562,9 @@ class CommandHumanizer implements CommandVisitor {
cmd.eventTargetAndNames, cmd.eventTargetAndNames,
cmd.variableNameAndValues, cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType), cmd.directives.map(checkAndStringifyType),
cmd.nativeShadow, serializeEnum(cmd.encapsulation),
cmd.ngContentIndex, cmd.ngContentIndex,
cmd.template.id cmd.templateId
]); ]);
return null; return null;
} }
@ -597,6 +590,8 @@ class CommandHumanizer implements CommandVisitor {
function createTestableModule(source: SourceExpression): SourceModule { function createTestableModule(source: SourceExpression): SourceModule {
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`; var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
var testableSource = `${source.declarations.join('\n')} var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`; ${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;
`;
return new SourceModule(null, testableSource); return new SourceModule(null, testableSource);
} }

View File

@ -18,7 +18,7 @@ import {PromiseWrapper} from 'angular2/src/facade/async';
import {SpyProtoViewFactory} from '../core/spies'; import {SpyProtoViewFactory} from '../core/spies';
import { import {
CompiledHostTemplate, CompiledHostTemplate,
CompiledTemplate, CompiledComponentTemplate,
BeginComponentCmd BeginComponentCmd
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler'; import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
@ -37,7 +37,7 @@ export function main() {
beforeEachBindings(() => { beforeEachBindings(() => {
protoViewFactorySpy = new SpyProtoViewFactory(); protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null); someProtoView = new AppProtoView(null, null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView); protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
return [provide(ProtoViewFactory, {useValue: protoViewFactorySpy})]; return [provide(ProtoViewFactory, {useValue: protoViewFactorySpy})];
}); });
@ -52,8 +52,7 @@ export function main() {
}); });
compiler.compileInHost(SomeComponent) compiler.compileInHost(SomeComponent)
.then((_) => { .then((_) => {
var beginComponentCmd = var beginComponentCmd = <BeginComponentCmd>cht.template.commands[0];
<BeginComponentCmd>cht.getTemplate().getData('app1').commands[0];
expect(beginComponentCmd.name).toEqual('some-comp'); expect(beginComponentCmd.name).toEqual('some-comp');
async.done(); async.done();
}); });

View File

@ -1,2 +1,4 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
// used by style_compiler_spec.ts // used by style_compiler_spec.ts
export var STYLES = ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']; export const STYLES = CONST_EXPR(['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']);

View File

@ -1,2 +1,4 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
// used by style_compiler_spec.ts // used by style_compiler_spec.ts
export var STYLES = ['span {color: blue}']; export const STYLES = CONST_EXPR(['span {color: blue}']);

View File

@ -17,7 +17,7 @@ import {SpyXHR} from './spies';
import {XHR} from 'angular2/src/compiler/xhr'; import {XHR} from 'angular2/src/compiler/xhr';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions'; import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {CONST_EXPR, isPresent, isBlank, StringWrapper} from 'angular2/src/facade/lang'; import {CONST_EXPR, isPresent, isBlank, StringWrapper, isArray} from 'angular2/src/facade/lang';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {evalModule} from './eval_module'; import {evalModule} from './eval_module';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler'; import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
@ -42,8 +42,6 @@ var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
export function main() { export function main() {
describe('StyleCompiler', () => { describe('StyleCompiler', () => {
var xhr: SpyXHR; var xhr: SpyXHR;
var templateId;
var appId;
beforeEachBindings(() => { beforeEachBindings(() => {
xhr = <any>new SpyXHR(); xhr = <any>new SpyXHR();
@ -52,11 +50,7 @@ export function main() {
var compiler: StyleCompiler; var compiler: StyleCompiler;
beforeEach(inject([StyleCompiler], (_compiler) => { beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
templateId = 23;
appId = 'app1';
compiler = _compiler;
}));
describe('compileComponentRuntime', () => { describe('compileComponentRuntime', () => {
var xhrUrlResults; var xhrUrlResults;
@ -82,10 +76,8 @@ export function main() {
} }
return PromiseWrapper.resolve(response); return PromiseWrapper.resolve(response);
}); });
return compiler.compileComponentRuntime( return compiler.compileComponentRuntime(new CompileTemplateMetadata(
appId, templateId, {styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
} }
describe('no shim', () => { describe('no shim', () => {
@ -102,7 +94,7 @@ export function main() {
it('should allow to import rules', inject([AsyncTestCompleter], (async) => { it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation) compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => { .then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done(); async.done();
}); });
})); }));
@ -111,7 +103,7 @@ export function main() {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation) compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation)
.then(styles => { .then(styles => {
expect(styles) expect(styles)
.toEqual(['div {color: red}', 'a {color: green}', 'span {color: blue}']); .toEqual(['div {color: red}', ['a {color: green}', ['span {color: blue}']]]);
async.done(); async.done();
}); });
})); }));
@ -124,8 +116,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}', 'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}' 'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -135,8 +127,8 @@ export function main() {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation) compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}', 'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {color: blue}' ['span[_ngcontent-%COMP%] {color: blue}']
]); ]);
async.done(); async.done();
}); });
@ -147,9 +139,11 @@ export function main() {
encapsulation) encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}', 'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'a[_ngcontent-app1-23] {color: green}', [
'span[_ngcontent-app1-23] {color: blue}' 'a[_ngcontent-%COMP%] {color: green}',
['span[_ngcontent-%COMP%] {color: blue}']
]
]); ]);
async.done(); async.done();
}); });
@ -162,8 +156,8 @@ export function main() {
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None) compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
]) ])
.then((styleArrays) => { .then((styleArrays) => {
expect(styleArrays[0]).toEqual(['span {color: blue}']); expect(styleArrays[0]).toEqual([['span {color: blue}']]);
expect(styleArrays[1]).toEqual(['span {color: blue}']); expect(styleArrays[1]).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1); expect(xhrCount).toBe(1);
async.done(); async.done();
}); });
@ -175,8 +169,8 @@ export function main() {
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}'; xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None) return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((styles1) => { .then((styles1) => {
expect(styles0).toEqual(['span {color: blue}']); expect(styles0).toEqual([['span {color: blue}']]);
expect(styles1).toEqual(['span {color: blue}']); expect(styles1).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1); expect(xhrCount).toBe(1);
async.done(); async.done();
}); });
@ -192,7 +186,7 @@ export function main() {
}) })
.then((styles) => { .then((styles) => {
expect(xhrCount).toBe(2); expect(xhrCount).toBe(2);
expect(styles).toEqual(['span {color: black}']); expect(styles).toEqual([['span {color: black}']]);
async.done(); async.done();
}); });
})); }));
@ -201,10 +195,8 @@ export function main() {
describe('compileComponentCodeGen', () => { describe('compileComponentCodeGen', () => {
function compile(styles: string[], styleAbsUrls: string[], function compile(styles: string[], styleAbsUrls: string[],
encapsulation: ViewEncapsulation): Promise<string[]> { encapsulation: ViewEncapsulation): Promise<string[]> {
var sourceExpression = compiler.compileComponentCodeGen( var sourceExpression = compiler.compileComponentCodeGen(new CompileTemplateMetadata(
`'${appId}'`, `${templateId}`, {styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports(); var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null); return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
}; };
@ -232,7 +224,7 @@ export function main() {
it('should allow to import rules', inject([AsyncTestCompleter], (async) => { it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation) compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => { .then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']); expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done(); async.done();
}); });
}), 1000); }), 1000);
@ -245,8 +237,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation) compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}', 'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}' 'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]); ]);
async.done(); async.done();
}); });
@ -256,8 +248,8 @@ export function main() {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation) compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => { .then(styles => {
compareStyles(styles, [ compareStyles(styles, [
'div[_ngcontent-app1-23] {color: red}', 'div[_ngcontent-%COMP%] {color: red}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}' ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
]); ]);
async.done(); async.done();
}); });
@ -289,10 +281,10 @@ export function main() {
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`) compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
.then(stylesAndShimStyles => { .then(stylesAndShimStyles => {
var expected = [ var expected = [
['div {color: red}', 'span {color: blue}'], ['div {color: red}', ['span {color: blue}']],
[ [
'div[_ngcontent-%COMP%] {color: red}', 'div[_ngcontent-%COMP%] {color: red}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}' ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
] ]
]; ];
compareStyles(stylesAndShimStyles[0], expected[0]); compareStyles(stylesAndShimStyles[0], expected[0]);
@ -307,20 +299,27 @@ export function main() {
function testableExpression(source: SourceExpression): SourceModule { function testableExpression(source: SourceExpression): SourceModule {
var testableSource = `${source.declarations.join('\n')} var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], source.expression)};`; ${codeGenValueFn(['_'], source.expression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(null, testableSource); return new SourceModule(null, testableSource);
} }
function testableModule(sourceModule: SourceModule): SourceModule { function testableModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs} var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`; ${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource); return new SourceModule(sourceModule.moduleUrl, testableSource);
} }
// Needed for Android browsers which add an extra space at the end of some lines // Needed for Android browsers which add an extra space at the end of some lines
function compareStyles(styles: string[], expectedStyles: string[]) { function compareStyles(styles: Array<string | any[]>, expectedStyles: Array<string | any[]>) {
expect(styles.length).toEqual(expectedStyles.length); expect(styles.length).toEqual(expectedStyles.length);
for (var i = 0; i < styles.length; i++) { for (var i = 0; i < styles.length; i++) {
expect(StringWrapper.replaceAll(styles[i], /\s+\n/g, '\n')).toEqual(expectedStyles[i]); var style = styles[i];
if (isArray(style)) {
compareStyles(<any[]>style, <any[]>expectedStyles[i]);
} else {
expect(StringWrapper.replaceAll(<string>style, /\s+\n/g, '\n')).toEqual(expectedStyles[i]);
}
} }
} }

View File

@ -39,7 +39,7 @@ import {
EmbeddedTemplateCmd, EmbeddedTemplateCmd,
TemplateCmd, TemplateCmd,
visitAllCommands, visitAllCommands,
CompiledTemplate CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import {Component, View, Directive, provide} from 'angular2/core'; import {Component, View, Directive, provide} from 'angular2/core';
@ -47,20 +47,17 @@ import {Component, View, Directive, provide} from 'angular2/core';
import {TEST_PROVIDERS} from './test_bindings'; import {TEST_PROVIDERS} from './test_bindings';
import {TestDispatcher, TestPipes} from './change_detector_mocks'; import {TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util'; import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
import {APP_ID} from 'angular2/src/core/application_tokens';
// Attention: This path has to point to this test file! // Attention: This path has to point to this test file!
const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec'; const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`); var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
const APP_ID_VALUE = 'app1';
export function main() { export function main() {
describe('TemplateCompiler', () => { describe('TemplateCompiler', () => {
var compiler: TemplateCompiler; var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver; var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachBindings(() => [provide(APP_ID, {useValue: APP_ID_VALUE}), TEST_PROVIDERS]); beforeEachBindings(() => TEST_PROVIDERS);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver], beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => { (_compiler, _runtimeMetadataResolver) => {
compiler = _compiler; compiler = _compiler;
@ -127,10 +124,10 @@ export function main() {
})); }));
} }
describe('compileHostComponentRuntime', () => { xdescribe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> { function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0]) return compiler.compileHostComponentRuntime(components[0])
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.getTemplate())); .then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.template));
} }
runTests(compile); runTests(compile);
@ -315,38 +312,39 @@ class NonComponent {
function testableTemplateModule(sourceModule: SourceModule, function testableTemplateModule(sourceModule: SourceModule,
normComp: CompileDirectiveMetadata): SourceModule { normComp: CompileDirectiveMetadata): SourceModule {
var resultExpression = var resultExpression =
`${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template.getTemplate())`; `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template.template)`;
var testableSource = `${sourceModule.sourceWithModuleRefs} var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`; ${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource); return new SourceModule(sourceModule.moduleUrl, testableSource);
} }
function testableStylesModule(sourceModule: SourceModule): SourceModule { function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs} var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`; ${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource); return new SourceModule(sourceModule.moduleUrl, testableSource);
} }
// Attention: read by eval! // Attention: read by eval!
export function humanizeTemplate( export function humanizeTemplate(
template: CompiledTemplate, template: CompiledComponentTemplate,
humanizedTemplates: Map<number, {[key: string]: any}> = null): {[key: string]: any} { humanizedTemplates: Map<string, {[key: string]: any}> = null): {[key: string]: any} {
if (isBlank(humanizedTemplates)) { if (isBlank(humanizedTemplates)) {
humanizedTemplates = new Map<number, {[key: string]: any}>(); humanizedTemplates = new Map<string, {[key: string]: any}>();
} }
var result = humanizedTemplates.get(template.id); var result = humanizedTemplates.get(template.id);
if (isPresent(result)) { if (isPresent(result)) {
return result; return result;
} }
var templateData = template.getData(APP_ID_VALUE);
var commands = []; var commands = [];
result = { result = {
'styles': templateData.styles, 'styles': template.styles,
'commands': commands, 'commands': commands,
'cd': testChangeDetector(templateData.changeDetectorFactory) 'cd': testChangeDetector(template.changeDetectorFactory)
}; };
humanizedTemplates.set(template.id, result); humanizedTemplates.set(template.id, result);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), templateData.commands); visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
return result; return result;
} }
@ -373,7 +371,7 @@ function testChangeDetector(changeDetectorFactory: Function): string[] {
class CommandHumanizer implements CommandVisitor { class CommandHumanizer implements CommandVisitor {
constructor(private result: any[], constructor(private result: any[],
private humanizedTemplates: Map<number, {[key: string]: any}>) {} private humanizedTemplates: Map<string, {[key: string]: any}>) {}
visitText(cmd: TextCmd, context: any): any { visitText(cmd: TextCmd, context: any): any {
this.result.push(`#text(${cmd.value})`); this.result.push(`#text(${cmd.value})`);
return null; return null;
@ -389,7 +387,7 @@ class CommandHumanizer implements CommandVisitor {
} }
visitBeginComponent(cmd: BeginComponentCmd, context: any): any { visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push(`<${cmd.name}>`); this.result.push(`<${cmd.name}>`);
this.result.push(humanizeTemplate(cmd.template, this.humanizedTemplates)); this.result.push(humanizeTemplate(cmd.templateGetter(), this.humanizedTemplates));
return null; return null;
} }
visitEndComponent(context: any): any { return this.visitEndElement(context); } visitEndComponent(context: any): any { return this.visitEndElement(context); }

View File

@ -75,14 +75,16 @@ export function main() {
describe(`${cdType} Change Detector`, () => { describe(`${cdType} Change Detector`, () => {
function _getProtoChangeDetector(def: ChangeDetectorDefinition) { function _getChangeDetectorFactory(def: ChangeDetectorDefinition) {
switch (cdType) { switch (cdType) {
case 'dynamic': case 'dynamic':
return new DynamicProtoChangeDetector(def); var dynProto = new DynamicProtoChangeDetector(def);
return (dispatcher) => dynProto.instantiate(dispatcher);
case 'JIT': case 'JIT':
return new JitProtoChangeDetector(def); var jitProto = new JitProtoChangeDetector(def);
return (dispatcher) => jitProto.instantiate(dispatcher);
case 'Pregen': case 'Pregen':
return getFactoryById(def.id)(def); return getFactoryById(def.id);
default: default:
return null; return null;
} }
@ -90,7 +92,7 @@ export function main() {
function _createWithoutHydrate(expression: string) { function _createWithoutHydrate(expression: string) {
var dispatcher = new TestDispatcher(); var dispatcher = new TestDispatcher();
var cd = _getProtoChangeDetector(getDefinition(expression).cdDef).instantiate(dispatcher); var cd = _getChangeDetectorFactory(getDefinition(expression).cdDef)(dispatcher);
return new _ChangeDetectorAndDispatcher(cd, dispatcher); return new _ChangeDetectorAndDispatcher(cd, dispatcher);
} }
@ -99,8 +101,7 @@ export function main() {
registry = null, dispatcher = null) { registry = null, dispatcher = null) {
if (isBlank(dispatcher)) dispatcher = new TestDispatcher(); if (isBlank(dispatcher)) dispatcher = new TestDispatcher();
var testDef = getDefinition(expression); var testDef = getDefinition(expression);
var protoCd = _getProtoChangeDetector(testDef.cdDef); var cd = _getChangeDetectorFactory(testDef.cdDef)(dispatcher);
var cd = protoCd.instantiate(dispatcher);
cd.hydrate(context, testDef.locals, null, registry); cd.hydrate(context, testDef.locals, null, registry);
return new _ChangeDetectorAndDispatcher(cd, dispatcher); return new _ChangeDetectorAndDispatcher(cd, dispatcher);
} }

View File

@ -22,7 +22,7 @@ void main(List<String> args) {
buf.write(','); buf.write(',');
} }
buf.write(" '''${_escape(allDefs[i].cdDef.id)}''': " buf.write(" '''${_escape(allDefs[i].cdDef.id)}''': "
"$className.$PROTO_CHANGE_DETECTOR_FACTORY_METHOD"); "$className.$CHANGE_DETECTOR_FACTORY_METHOD");
} }
buf.write('};'); buf.write('};');
print(new DartFormatter().format(''' print(new DartFormatter().format('''

View File

@ -17,7 +17,7 @@ import {Component, View, provide} from 'angular2/core';
import {SpyProtoViewFactory} from '../spies'; import {SpyProtoViewFactory} from '../spies';
import { import {
CompiledHostTemplate, CompiledHostTemplate,
CompiledTemplate, CompiledComponentTemplate,
BeginComponentCmd BeginComponentCmd
} from 'angular2/src/core/linker/template_commands'; } from 'angular2/src/core/linker/template_commands';
import {Compiler} from 'angular2/src/core/linker/compiler'; import {Compiler} from 'angular2/src/core/linker/compiler';
@ -35,7 +35,7 @@ export function main() {
beforeEachBindings(() => { beforeEachBindings(() => {
protoViewFactorySpy = new SpyProtoViewFactory(); protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null); someProtoView = new AppProtoView(null, null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView); protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
var factory = provide(ProtoViewFactory, {useValue: protoViewFactorySpy}); var factory = provide(ProtoViewFactory, {useValue: protoViewFactorySpy});
var classProvider = provide(Compiler, {useClass: Compiler_}); var classProvider = provide(Compiler, {useClass: Compiler_});
@ -45,7 +45,7 @@ export function main() {
beforeEach(inject([Compiler], (_compiler) => { beforeEach(inject([Compiler], (_compiler) => {
compiler = _compiler; compiler = _compiler;
cht = new CompiledHostTemplate(() => new CompiledTemplate(23, null)); cht = new CompiledHostTemplate(new CompiledComponentTemplate('aCompId', null, null, null));
reflector.registerType(SomeComponent, new ReflectionInfo([cht])); reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
})); }));

View File

@ -444,6 +444,27 @@ export function main() {
})); }));
} }
if (DOM.supportsDOMEvents()) {
it('should support emulated style encapsulation',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MainComp, new ViewMetadata({
template: '<div></div>',
styles: ['div { color: red}'],
encapsulation: ViewEncapsulation.Emulated
}))
.createAsync(MainComp)
.then((main) => {
var mainEl = main.debugElement.nativeElement;
var div1 = DOM.firstChild(mainEl);
var div2 = DOM.createElement('div');
DOM.appendChild(mainEl, div2);
expect(DOM.getComputedStyle(div1).color).toEqual('rgb(255, 0, 0)');
expect(DOM.getComputedStyle(div2).color).toEqual('rgb(0, 0, 0)');
async.done();
});
}));
}
it('should support nested conditionals that contain ng-contents', it('should support nested conditionals that contain ng-contents',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MainComp, new ViewMetadata({ tcb.overrideView(MainComp, new ViewMetadata({

View File

@ -250,8 +250,8 @@ function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
if (isBlank(binders)) { if (isBlank(binders)) {
binders = []; binders = [];
} }
var res = new AppProtoView([], type, true, (_) => new SpyChangeDetector(), new Map<string, any>(), var res = new AppProtoView(null, [], type, true, (_) => new SpyChangeDetector(),
null); new Map<string, any>(), null);
var mergedElementCount = 0; var mergedElementCount = 0;
var mergedEmbeddedViewCount = 0; var mergedEmbeddedViewCount = 0;
var mergedViewCount = 1; var mergedViewCount = 1;

View File

@ -24,7 +24,9 @@ export function main() {
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); } function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
function createProtoView() { return new AppProtoView(null, null, null, null, null, null); } function createProtoView() {
return new AppProtoView(null, null, null, null, null, null, null);
}
function createView(pv) { function createView(pv) {
return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null); return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null);

View File

@ -14,132 +14,150 @@ import {
import {isPresent} from 'angular2/src/facade/lang'; import {isPresent} from 'angular2/src/facade/lang';
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import * as appCmds from 'angular2/src/core/linker/template_commands'; import * as appCmds from 'angular2/src/core/linker/template_commands';
import {createRenderView, NodeFactory} from 'angular2/src/core/render/view_factory'; import {
import {RenderTemplateCmd, RenderBeginElementCmd} from 'angular2/src/core/render/api'; createRenderView,
encapsulateStyles,
NodeFactory
} from 'angular2/src/core/render/view_factory';
import {
RenderTemplateCmd,
RenderBeginElementCmd,
RenderComponentTemplate
} from 'angular2/src/core/render/api';
import {SpyRenderEventDispatcher} from '../spies'; import {SpyRenderEventDispatcher} from '../spies';
import {DOM} from 'angular2/src/core/dom/dom_adapter'; import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
function beginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], function beginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
isBound: boolean, ngContentIndex: number): RenderBeginElementCmd { isBound: boolean, ngContentIndex: number): RenderBeginElementCmd {
return appCmds.beginElement(name, attrNameAndValues, eventTargetAndNames, [], [], isBound, return new appCmds.BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, [], [], isBound,
ngContentIndex) ngContentIndex)
} }
function endElement() { function endElement() {
return appCmds.endElement(); return new appCmds.EndElementCmd();
} }
function text(value: string, isBound: boolean, ngContentIndex: number) { function text(value: string, isBound: boolean, ngContentIndex: number) {
return appCmds.text(value, isBound, ngContentIndex); return new appCmds.TextCmd(value, isBound, ngContentIndex);
} }
function embeddedTemplate(attrNameAndValues: string[], isMerged: boolean, ngContentIndex: number, function embeddedTemplate(attrNameAndValues: string[], isMerged: boolean, ngContentIndex: number,
children: any[]) { children: any[]) {
return appCmds.embeddedTemplate(attrNameAndValues, [], [], isMerged, ngContentIndex, null, return new appCmds.EmbeddedTemplateCmd(attrNameAndValues, [], [], isMerged, ngContentIndex, null,
children); children);
} }
function beginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[], function beginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
nativeShadow: boolean, ngContentIndex: number, templateId: number) { ngContentIndex: number, templateId: string) {
return appCmds.beginComponent(name, attrNameAndValues, eventTargetAndNames, [], [], nativeShadow, return new appCmds.BeginComponentCmd(
ngContentIndex, new appCmds.CompiledTemplate(templateId, null)); name, attrNameAndValues, eventTargetAndNames, [], [], null, ngContentIndex,
() => new appCmds.CompiledComponentTemplate(templateId, null, null, null));
} }
function endComponent() { function endComponent() {
return appCmds.endComponent(); return new appCmds.EndComponentCmd();
} }
function ngContent(index: number, ngContentIndex: number) { function ngContent(index: number, ngContentIndex: number) {
return appCmds.ngContent(index, ngContentIndex); return new appCmds.NgContentCmd(index, ngContentIndex);
} }
export function main() { export function main() {
describe('createRenderView', () => { describe('createRenderView', () => {
var nodeFactory: DomNodeFactory; var nodeFactory: DomNodeFactory;
var eventDispatcher: SpyRenderEventDispatcher; var eventDispatcher: SpyRenderEventDispatcher;
var componentTemplates = new Map<number, RenderTemplateCmd[]>(); var componentTemplates = new Map<string, RenderComponentTemplate | RenderTemplateCmd[]>();
var defaultCmpTpl: RenderComponentTemplate;
beforeEach(() => { beforeEach(() => {
nodeFactory = new DomNodeFactory(componentTemplates); nodeFactory = new DomNodeFactory(componentTemplates);
eventDispatcher = new SpyRenderEventDispatcher(); eventDispatcher = new SpyRenderEventDispatcher();
defaultCmpTpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], []);
}); });
describe('primitives', () => { describe('primitives', () => {
it('should create elements with attributes', () => { it('should create elements with attributes', () => {
var view = createRenderView( var view = createRenderView(
defaultCmpTpl,
[beginElement('div', ['attr1', 'value1'], [], false, null), endElement()], null, [beginElement('div', ['attr1', 'value1'], [], false, null), endElement()], null,
nodeFactory); nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div attr1="value1"></div>'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div attr1="value1"></div>');
}); });
it('should create host elements with attributes', () => { it('should create host elements with attributes', () => {
componentTemplates.set(0, []); componentTemplates.set('0', []);
var view = createRenderView( var view = createRenderView(
[beginComponent('a-comp', ['attr1', 'value1'], [], false, null, 0), endElement()], null, defaultCmpTpl,
[beginComponent('a-comp', ['attr1', 'value1'], [], null, '0'), endElement()], null,
nodeFactory); nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<a-comp attr1="value1"></a-comp>'); .toEqual('<a-comp attr1="value1"></a-comp>');
}); });
it('should create embedded templates with attributes', () => { it('should create embedded templates with attributes', () => {
componentTemplates.set(0, []); componentTemplates.set('0', []);
var view = createRenderView([embeddedTemplate(['attr1', 'value1'], false, null, [])], null, var view = createRenderView(defaultCmpTpl,
[embeddedTemplate(['attr1', 'value1'], false, null, [])], null,
nodeFactory); nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<template attr1="value1"></template>'); .toEqual('<template attr1="value1"></template>');
}); });
it('should store bound elements', () => { it('should store bound elements', () => {
componentTemplates.set(0, []); componentTemplates.set('0', []);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('div', ['id', '1'], [], false, null), beginElement('div', ['id', '1'], [], false, null),
endElement(), endElement(),
beginElement('span', ['id', '2'], [], true, null), beginElement('span', ['id', '2'], [], true, null),
endElement(), endElement(),
beginComponent('a-comp', ['id', '3'], [], false, null, 0), beginComponent('a-comp', ['id', '3'], [], null, '0'),
endElement(), endElement(),
embeddedTemplate(['id', '4'], false, null, []) embeddedTemplate(['id', '4'], false, null, [])
], ],
null, nodeFactory); null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '3', '4']); expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '3', '4']);
}); });
it('should use the inplace element for the first create element', () => { it('should use the inplace element for the first create element', () => {
var el = DOM.createElement('span'); var el = DOM.createElement('span');
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('div', ['attr1', 'value1'], [], false, null), beginElement('div', ['attr1', 'value1'], [], false, null),
endElement(), endElement(),
beginElement('div', [], [], false, null), beginElement('div', [], [], false, null),
endElement() endElement()
], ],
el, nodeFactory); el, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<span attr1="value1"></span><div></div>'); .toEqual('<span attr1="value1"></span><div></div>');
}); });
it('should create text nodes', () => { it('should create text nodes', () => {
var view = createRenderView([text('someText', false, null)], null, nodeFactory); var view =
createRenderView(defaultCmpTpl, [text('someText', false, null)], null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('someText'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('someText');
}); });
it('should store bound text nodes', () => { it('should store bound text nodes', () => {
var view = var view = createRenderView(defaultCmpTpl, [text('1', false, null), text('2', true, null)],
createRenderView([text('1', false, null), text('2', true, null)], null, nodeFactory); null, nodeFactory);
expect(stringifyElement(view.boundTextNodes[0])).toEqual('2'); expect(stringifyElement(view.boundTextNodes[0])).toEqual('2');
}); });
it('should register element event listeners', () => { it('should register element event listeners', () => {
componentTemplates.set(0, []); componentTemplates.set('0', []);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('div', [], [null, 'click'], true, null), beginElement('div', [], [null, 'click'], true, null),
endElement(), endElement(),
beginComponent('a-comp', [], [null, 'click'], false, null, 0), beginComponent('a-comp', [], [null, 'click'], null, '0'),
endElement(), endElement(),
], ],
null, nodeFactory); null, nodeFactory);
view.setEventDispatcher(<any>eventDispatcher); view.setEventDispatcher(<any>eventDispatcher);
var event = {}; var event = {};
nodeFactory.triggerLocalEvent(view.boundElements[0], 'click', event); nodeFactory.triggerLocalEvent(view.boundElements[0], 'click', event);
@ -151,14 +169,14 @@ export function main() {
}); });
it('should register element global event listeners', () => { it('should register element global event listeners', () => {
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('div', [], ['window', 'scroll'], true, null), beginElement('div', [], ['window', 'scroll'], true, null),
endElement(), endElement(),
beginComponent('a-comp', [], ['window', 'scroll'], false, null, 0), beginComponent('a-comp', [], ['window', 'scroll'], null, '0'),
endElement(), endElement(),
], ],
null, nodeFactory); null, nodeFactory);
view.hydrate(); view.hydrate();
view.setEventDispatcher(<any>eventDispatcher); view.setEventDispatcher(<any>eventDispatcher);
var event = {}; var event = {};
@ -175,67 +193,72 @@ export function main() {
describe('nested nodes', () => { describe('nested nodes', () => {
it('should create nested node', () => { it('should create nested node', () => {
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('a', [], [], false, null), beginElement('a', [], [], false, null),
beginElement('b', [], [], false, null), beginElement('b', [], [], false, null),
text('someText', false, null), text('someText', false, null),
endElement(), endElement(),
endElement(), endElement(),
], ],
null, nodeFactory); null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<a><b>someText</b></a>'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<a><b>someText</b></a>');
}); });
it('should store bound elements in depth first order', () => { it('should store bound elements in depth first order', () => {
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginElement('a', ['id', '1'], [], false, null), beginElement('a', ['id', '1'], [], false, null),
endElement(), endElement(),
beginElement('a', ['id', '2'], [], true, null), beginElement('a', ['id', '2'], [], true, null),
beginElement('a', ['id', '3'], [], false, null), beginElement('a', ['id', '3'], [], false, null),
endElement(), endElement(),
beginElement('a', ['id', '4'], [], true, null), beginElement('a', ['id', '4'], [], true, null),
endElement(), endElement(),
endElement(), endElement(),
beginElement('a', ['id', '5'], [], false, null), beginElement('a', ['id', '5'], [], false, null),
endElement(), endElement(),
beginElement('a', ['id', '6'], [], true, null), beginElement('a', ['id', '6'], [], true, null),
endElement(), endElement(),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '4', '6']); expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '4', '6']);
}); });
it('should store bound text nodes in depth first order', () => { it('should store bound text nodes in depth first order', () => {
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
text('1', false, null), text('1', false, null),
text('2', true, null), text('2', true, null),
beginElement('a', [], [], false, null), beginElement('a', [], [], false, null),
text('3', false, null), text('3', false, null),
text('4', true, null), text('4', true, null),
endElement(), endElement(),
text('5', false, null), text('5', false, null),
text('6', true, null), text('6', true, null),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapText(view.boundTextNodes)).toEqual(['2', '4', '6']); expect(mapText(view.boundTextNodes)).toEqual(['2', '4', '6']);
}); });
}); });
describe('merged embedded templates', () => { describe('merged embedded templates',
it('should create separate fragments', () => { () => {
var view = createRenderView( it('should create separate fragments', () => {
[embeddedTemplate(['attr1', 'value1'], true, null, [text('someText', false, null)])], var view = createRenderView(defaultCmpTpl,
null, nodeFactory); [
expect(view.fragments.length).toBe(2); embeddedTemplate(['attr1', 'value1'], true, null,
expect(stringifyFragment(view.fragments[1].nodes)).toEqual('someText'); [text('someText', false, null)])
}); ],
null, nodeFactory);
expect(view.fragments.length).toBe(2);
expect(stringifyFragment(view.fragments[1].nodes)).toEqual('someText');
});
it('should store bound elements after the bound elements of earlier fragments', () => { it('should store bound elements after the bound elements of earlier fragments',
var view = () => {
createRenderView( var view =
createRenderView(defaultCmpTpl,
[ [
beginElement('a', ['id', '1.1'], [], true, null), beginElement('a', ['id', '1.1'], [], true, null),
endElement(), endElement(),
@ -254,13 +277,14 @@ export function main() {
endElement(), endElement(),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')) expect(mapAttrs(view.boundElements, 'id'))
.toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1']); .toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1']);
}); });
it('should store bound text nodes after the bound text nodes of earlier fragments', () => { it('should store bound text nodes after the bound text nodes of earlier fragments',
var view = () => {
createRenderView( var view =
createRenderView(defaultCmpTpl,
[ [
text('1.1', true, null), text('1.1', true, null),
embeddedTemplate(['id', '1.2'], true, null, embeddedTemplate(['id', '1.2'], true, null,
@ -275,23 +299,25 @@ export function main() {
text('1.2', true, null), text('1.2', true, null),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapText(view.boundTextNodes)).toEqual(['1.1', '1.2', '2.1', '2.2', '3.1']); expect(mapText(view.boundTextNodes))
}); .toEqual(['1.1', '1.2', '2.1', '2.2', '3.1']);
});
}); });
describe('non merged embedded templates', () => { describe('non merged embedded templates', () => {
it('should only create the anchor element', () => { it('should only create the anchor element', () => {
var view = createRenderView( var view =
[ createRenderView(defaultCmpTpl,
embeddedTemplate(['id', '1.1'], false, null, [
[ embeddedTemplate(['id', '1.1'], false, null,
text('someText', true, null), [
beginElement('a', ['id', '2.1'], [], true, null), text('someText', true, null),
endElement() beginElement('a', ['id', '2.1'], [], true, null),
]) endElement()
], ])
null, nodeFactory); ],
null, nodeFactory);
expect(view.fragments.length).toBe(1); expect(view.fragments.length).toBe(1);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<template id="1.1"></template>'); .toEqual('<template id="1.1"></template>');
@ -302,46 +328,52 @@ export function main() {
describe('components', () => { describe('components', () => {
it('should store the component template in the same fragment', () => { it('should store the component template in the same fragment', () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
text('hello', false, null), text('hello', false, null),
]); ]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[beginComponent('my-comp', [], [], false, null, 0), endComponent()], null, nodeFactory); [beginComponent('my-comp', [], [], null, '0'), endComponent()],
null, nodeFactory);
expect(view.fragments.length).toBe(1); expect(view.fragments.length).toBe(1);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>hello</my-comp>'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>hello</my-comp>');
}); });
it('should use native shadow DOM', () => { it('should use native shadow DOM', () => {
componentTemplates.set(0, [ componentTemplates.set(
text('hello', false, null), '0', new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native,
]); [
var view = createRenderView( text('hello', false, null),
[beginComponent('my-comp', [], [], true, null, 0), endComponent()], null, nodeFactory); ],
[]));
var view = createRenderView(defaultCmpTpl,
[beginComponent('my-comp', [], [], null, '0'), endComponent()],
null, nodeFactory);
expect(view.fragments.length).toBe(1); expect(view.fragments.length).toBe(1);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<my-comp><shadow-root>hello</shadow-root></my-comp>'); .toEqual('<my-comp><shadow-root>hello</shadow-root></my-comp>');
}); });
it('should store bound elements after the bound elements of the main template', () => { it('should store bound elements after the bound elements of the main template', () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
beginComponent('b-comp', ['id', '2.1'], [], false, null, 1), beginComponent('b-comp', ['id', '2.1'], [], null, '1'),
endComponent(), endComponent(),
beginComponent('b-comp', ['id', '2.2'], [], false, null, 1), beginComponent('b-comp', ['id', '2.2'], [], null, '1'),
endComponent(), endComponent(),
]); ]);
componentTemplates.set(1, [beginElement('a', ['id', '3.1'], [], true, null), endElement()]); componentTemplates.set('1',
var view = createRenderView( [beginElement('a', ['id', '3.1'], [], true, null), endElement()]);
[ var view = createRenderView(defaultCmpTpl,
beginElement('a', ['id', '1.1'], [], true, null), [
endElement(), beginElement('a', ['id', '1.1'], [], true, null),
beginComponent('a-comp', ['id', '1.2'], [], false, null, 0), endElement(),
beginElement('a', ['id', '1.3'], [], true, null), beginComponent('a-comp', ['id', '1.2'], [], null, '0'),
endElement(), beginElement('a', ['id', '1.3'], [], true, null),
endComponent(), endElement(),
beginElement('a', ['id', '1.4'], [], true, null), endComponent(),
endElement(), beginElement('a', ['id', '1.4'], [], true, null),
], endElement(),
null, nodeFactory); ],
null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')) expect(mapAttrs(view.boundElements, 'id'))
.toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']); .toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']);
@ -349,64 +381,64 @@ export function main() {
it('should store bound elements from the view before bound elements from content components', it('should store bound elements from the view before bound elements from content components',
() => { () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
beginElement('a', ['id', '2.1'], [], true, null), beginElement('a', ['id', '2.1'], [], true, null),
endElement(), endElement(),
]); ]);
componentTemplates.set(1, [ componentTemplates.set('1', [
beginElement('a', ['id', '3.1'], [], true, null), beginElement('a', ['id', '3.1'], [], true, null),
endElement(), endElement(),
]); ]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginComponent('a-comp', ['id', '1.1'], [], false, null, 0), beginComponent('a-comp', ['id', '1.1'], [], null, '0'),
beginComponent('b-comp', ['id', '1.2'], [], false, null, 1), beginComponent('b-comp', ['id', '1.2'], [], null, '1'),
endComponent(), endComponent(),
endComponent(), endComponent(),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1', '1.2', '2.1', '3.1']); expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1', '1.2', '2.1', '3.1']);
}); });
it('should process nested components in depth first order', () => { it('should process nested components in depth first order', () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
beginComponent('b11-comp', ['id', '2.1'], [], false, null, 2), beginComponent('b11-comp', ['id', '2.1'], [], null, '2'),
endComponent(), endComponent(),
beginComponent('b12-comp', ['id', '2.2'], [], false, null, 3), beginComponent('b12-comp', ['id', '2.2'], [], null, '3'),
endComponent(), endComponent(),
]); ]);
componentTemplates.set(1, [ componentTemplates.set('1', [
beginComponent('b21-comp', ['id', '3.1'], [], false, null, 4), beginComponent('b21-comp', ['id', '3.1'], [], null, '4'),
endComponent(), endComponent(),
beginComponent('b22-comp', ['id', '3.2'], [], false, null, 5), beginComponent('b22-comp', ['id', '3.2'], [], null, '5'),
endComponent(), endComponent(),
]); ]);
componentTemplates.set(2, [ componentTemplates.set('2', [
beginElement('b11', ['id', '4.11'], [], true, null), beginElement('b11', ['id', '4.11'], [], true, null),
endElement(), endElement(),
]); ]);
componentTemplates.set(3, [ componentTemplates.set('3', [
beginElement('b12', ['id', '4.12'], [], true, null), beginElement('b12', ['id', '4.12'], [], true, null),
endElement(), endElement(),
]); ]);
componentTemplates.set(4, [ componentTemplates.set('4', [
beginElement('b21', ['id', '4.21'], [], true, null), beginElement('b21', ['id', '4.21'], [], true, null),
endElement(), endElement(),
]); ]);
componentTemplates.set(5, [ componentTemplates.set('5', [
beginElement('b22', ['id', '4.22'], [], true, null), beginElement('b22', ['id', '4.22'], [], true, null),
endElement(), endElement(),
]); ]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginComponent('a1-comp', ['id', '1.1'], [], false, null, 0), beginComponent('a1-comp', ['id', '1.1'], [], null, '0'),
endComponent(), endComponent(),
beginComponent('a2-comp', ['id', '1.2'], [], false, null, 1), beginComponent('a2-comp', ['id', '1.2'], [], null, '1'),
endComponent(), endComponent(),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapAttrs(view.boundElements, 'id')) expect(mapAttrs(view.boundElements, 'id'))
.toEqual(['1.1', '1.2', '2.1', '2.2', '4.11', '4.12', '3.1', '3.2', '4.21', '4.22']); .toEqual(['1.1', '1.2', '2.1', '2.2', '4.11', '4.12', '3.1', '3.2', '4.21', '4.22']);
@ -414,26 +446,26 @@ export function main() {
it('should store bound text nodes after the bound text nodes of the main template', () => { it('should store bound text nodes after the bound text nodes of the main template', () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
text('2.1', true, null), text('2.1', true, null),
beginComponent('b-comp', [], [], false, null, 1), beginComponent('b-comp', [], [], null, '1'),
endComponent(), endComponent(),
beginComponent('b-comp', [], [], false, null, 1), beginComponent('b-comp', [], [], null, '1'),
endComponent(), endComponent(),
text('2.2', true, null), text('2.2', true, null),
]); ]);
componentTemplates.set(1, [ componentTemplates.set('1', [
text('3.1', true, null), text('3.1', true, null),
]); ]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
text('1.1', true, null), text('1.1', true, null),
beginComponent('a-comp', [], [], false, null, 0), beginComponent('a-comp', [], [], null, '0'),
text('1.2', true, null), text('1.2', true, null),
endComponent(), endComponent(),
text('1.3', true, null), text('1.3', true, null),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapText(view.boundTextNodes)) expect(mapText(view.boundTextNodes))
.toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1', '3.1']); .toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1', '3.1']);
@ -442,77 +474,75 @@ export function main() {
it('should store bound text nodes from the view before bound text nodes from content components', it('should store bound text nodes from the view before bound text nodes from content components',
() => { () => {
componentTemplates.set(0, [text('2.1', true, null)]); componentTemplates.set('0', [text('2.1', true, null)]);
componentTemplates.set(1, [text('3.1', true, null)]); componentTemplates.set('1', [text('3.1', true, null)]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginComponent('a-comp', [], [], false, null, 0), beginComponent('a-comp', [], [], null, '0'),
beginComponent('b-comp', [], [], false, null, 1), beginComponent('b-comp', [], [], null, '1'),
endComponent(), endComponent(),
endComponent(), endComponent(),
], ],
null, nodeFactory); null, nodeFactory);
expect(mapText(view.boundTextNodes)).toEqual(['2.1', '3.1']); expect(mapText(view.boundTextNodes)).toEqual(['2.1', '3.1']);
}); });
describe('content projection', () => { describe('content projection', () => {
it('should remove non projected nodes', () => { it('should remove non projected nodes', () => {
componentTemplates.set(0, []); componentTemplates.set('0', []);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginComponent('my-comp', [], [], false, null, 0), beginComponent('my-comp', [], [], null, '0'),
text('hello', false, null), text('hello', false, null),
endComponent() endComponent()
], ],
null, nodeFactory); null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp></my-comp>'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp></my-comp>');
}); });
it('should keep non projected nodes in the light dom when using native shadow dom', () => { it('should keep non projected nodes in the light dom when using native shadow dom', () => {
componentTemplates.set(0, []); componentTemplates.set('0', new RenderComponentTemplate('someId', 'shortid',
var view = createRenderView( ViewEncapsulation.Native, [], []));
[ var view = createRenderView(defaultCmpTpl,
beginComponent('my-comp', [], [], true, null, 0), [
text('hello', false, null), beginComponent('my-comp', [], [], null, '0'),
endComponent() text('hello', false, null),
], endComponent()
null, nodeFactory); ],
null, nodeFactory);
var rootEl = view.fragments[0].nodes[0]; var rootEl = view.fragments[0].nodes[0];
expect(stringifyElement(rootEl)) expect(stringifyElement(rootEl))
.toEqual('<my-comp><shadow-root></shadow-root>hello</my-comp>'); .toEqual('<my-comp><shadow-root></shadow-root>hello</my-comp>');
}); });
it('should project commands based on their ngContentIndex', () => { it('should project commands based on their ngContentIndex', () => {
componentTemplates.set(0, [ componentTemplates.set('0', [
text('(', false, null), text('(', false, null),
ngContent(0, null), ngContent(0, null),
text(',', false, null), text(',', false, null),
ngContent(1, null), ngContent(1, null),
text(')', false, null) text(')', false, null)
]); ]);
var view = createRenderView( var view = createRenderView(defaultCmpTpl,
[ [
beginComponent('my-comp', [], [], false, null, 0), beginComponent('my-comp', [], [], null, '0'),
text('2', false, 1), text('2', false, 1),
text('1', false, 0), text('1', false, 0),
endComponent() endComponent()
], ],
null, nodeFactory); null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>(1,2)</my-comp>'); expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>(1,2)</my-comp>');
}); });
it('should reproject nodes over multiple ng-content commands', () => { it('should reproject nodes over multiple ng-content commands', () => {
componentTemplates.set( componentTemplates.set(
0, [beginComponent('b-comp', [], [], false, null, 1), ngContent(0, 0), endComponent()]); '0', [beginComponent('b-comp', [], [], null, '1'), ngContent(0, 0), endComponent()]);
componentTemplates.set( componentTemplates.set(
1, [text('(', false, null), ngContent(0, null), text(')', false, null)]); '1', [text('(', false, null), ngContent(0, null), text(')', false, null)]);
var view = createRenderView( var view = createRenderView(
[ defaultCmpTpl,
beginComponent('a-comp', [], [], false, null, 0), [beginComponent('a-comp', [], [], null, '0'), text('hello', false, 0), endComponent()],
text('hello', false, 0),
endComponent()
],
null, nodeFactory); null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)) expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<a-comp><b-comp>(hello)</b-comp></a-comp>'); .toEqual('<a-comp><b-comp>(hello)</b-comp></a-comp>');
@ -520,14 +550,97 @@ export function main() {
it('should store content injection points for root component in a view', () => { it('should store content injection points for root component in a view', () => {
componentTemplates.set(0, [ngContent(0, null)]); componentTemplates.set('0', [ngContent(0, null)]);
var view = var view = createRenderView(defaultCmpTpl,
createRenderView([beginComponent('a-comp', [], [], false, null, 0), endComponent()], [beginComponent('a-comp', [], [], null, '0'), endComponent()],
DOM.createElement('root'), nodeFactory); DOM.createElement('root'), nodeFactory);
expect(stringifyFragment(view.rootContentInsertionPoints)) expect(stringifyFragment(view.rootContentInsertionPoints))
.toEqual('<root-content-insertion-point></root-content-insertion-point>'); .toEqual('<root-content-insertion-point></root-content-insertion-point>');
}); });
}); });
describe('view encapsulation', () => {
it('should not add attributes to elements in template with ViewEncapsulation.None', () => {
var tpl = new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], []);
var view = createRenderView(tpl, [beginElement('div', [], [], false, null), endElement()],
null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div></div>');
});
it('should not add attributes to elements in template with ViewEncapsulation.Native', () => {
var tpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native, [], []);
var view = createRenderView(tpl, [beginElement('div', [], [], false, null), endElement()],
null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div></div>');
});
describe('ViewEncapsulation.Emulated', () => {
var encapsulatedTpl;
beforeEach(() => {
encapsulatedTpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Emulated, [], []);
});
it('should add marker attributes to content elements', () => {
var view = createRenderView(encapsulatedTpl,
[beginElement('div', [], [], false, null), endElement()],
null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<div _ngcontent-shortid=""></div>');
});
it('should add marker attributes to content elements in merged embedded templates', () => {
var view = createRenderView(
encapsulatedTpl,
[
embeddedTemplate([], true, null,
[beginElement('div', [], [], false, null), endElement()])
],
null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<template></template>');
expect(stringifyFragment(view.fragments[1].nodes))
.toEqual('<div _ngcontent-shortid=""></div>');
});
it('should add marker attributes to host elements and content elements of nested components',
() => {
componentTemplates.set(
'0', new RenderComponentTemplate(
'innerComp', 'innerid', ViewEncapsulation.Emulated,
[beginElement('div', [], [], false, null), endElement()], []));
var view = createRenderView(
encapsulatedTpl, [beginComponent('my-comp', [], [], null, '0'), endComponent()],
null, nodeFactory);
expect(stringifyFragment(view.fragments[0].nodes))
.toEqual(
'<my-comp _ngcontent-shortid="" _nghost-innerid=""><div _ngcontent-innerid=""></div></my-comp>');
});
});
});
});
describe('encapsulateStyles', () => {
const input = 'div[%COMP%] {}';
it('should not change styles for ViewEncapsulation.Native', () => {
var tpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native, [], [input]);
expect(encapsulateStyles(tpl)).toEqual([input]);
});
it('should not change styles for ViewEncapsulation.None', () => {
var tpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], [input]);
expect(encapsulateStyles(tpl)).toEqual([input]);
});
it('should change styles for ViewEncapsulation.Emulated', () => {
var tpl =
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Emulated, [], [input]);
expect(encapsulateStyles(tpl)).toEqual(['div[shortid] {}']);
});
}); });
} }
@ -535,7 +648,7 @@ class DomNodeFactory implements NodeFactory<Node> {
private _globalEventListeners: GlobalEventListener[] = []; private _globalEventListeners: GlobalEventListener[] = [];
private _localEventListeners: LocalEventListener[] = []; private _localEventListeners: LocalEventListener[] = [];
constructor(private _components: Map<number, RenderTemplateCmd[]>) {} constructor(private _components: Map<string, RenderComponentTemplate | RenderTemplateCmd[]>) {}
triggerLocalEvent(el: Element, eventName: string, event: any) { triggerLocalEvent(el: Element, eventName: string, event: any) {
this._localEventListeners.forEach(listener => { this._localEventListeners.forEach(listener => {
@ -553,8 +666,14 @@ class DomNodeFactory implements NodeFactory<Node> {
}); });
} }
resolveComponentTemplate(templateId: number): RenderTemplateCmd[] { resolveComponentTemplate(templateId: string): RenderComponentTemplate {
return this._components.get(templateId); var data = this._components.get(templateId);
if (data instanceof RenderComponentTemplate) {
return data;
} else {
return new RenderComponentTemplate(templateId, templateId, ViewEncapsulation.None,
<RenderTemplateCmd[]>data, []);
}
} }
createTemplateAnchor(attrNameAndValues: string[]): Node { createTemplateAnchor(attrNameAndValues: string[]): Node {
var el = DOM.createElement('template'); var el = DOM.createElement('template');
@ -575,7 +694,7 @@ class DomNodeFactory implements NodeFactory<Node> {
DOM.setAttribute(el, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]); DOM.setAttribute(el, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]);
} }
} }
createShadowRoot(host: Node, templateId: number): Node { createShadowRoot(host: Node, templateId: string): Node {
var root = DOM.createElement('shadow-root'); var root = DOM.createElement('shadow-root');
DOM.appendChild(host, root); DOM.appendChild(host, root);
return root; return root;

View File

@ -1274,34 +1274,36 @@ var NG_ALL = [
'PipeTransform:dart', 'PipeTransform:dart',
'RenderBeginCmd:dart', 'RenderBeginCmd:dart',
'RenderBeginCmd.isBound', 'RenderBeginCmd.isBound',
'RenderBeginCmd.isBound=',
'RenderBeginCmd.ngContentIndex', 'RenderBeginCmd.ngContentIndex',
'RenderBeginCmd.ngContentIndex=',
'RenderBeginComponentCmd:dart', 'RenderBeginComponentCmd:dart',
'RenderBeginComponentCmd.nativeShadow', 'RenderBeginComponentCmd.attrNameAndValues',
'RenderBeginComponentCmd.nativeShadow=', 'RenderBeginComponentCmd.eventTargetAndNames',
'RenderBeginComponentCmd.isBound',
'RenderBeginComponentCmd.name',
'RenderBeginComponentCmd.ngContentIndex',
'RenderBeginComponentCmd.templateId', 'RenderBeginComponentCmd.templateId',
'RenderBeginComponentCmd.templateId=',
'RenderBeginElementCmd:dart', 'RenderBeginElementCmd:dart',
'RenderBeginElementCmd.attrNameAndValues', 'RenderBeginElementCmd.attrNameAndValues',
'RenderBeginElementCmd.attrNameAndValues=', 'RenderBeginElementCmd.isBound',
'RenderBeginElementCmd.eventTargetAndNames', 'RenderBeginElementCmd.eventTargetAndNames',
'RenderBeginElementCmd.eventTargetAndNames=',
'RenderBeginElementCmd.name', 'RenderBeginElementCmd.name',
'RenderBeginElementCmd.name=', 'RenderBeginElementCmd.ngContentIndex',
'RenderCommandVisitor:dart', 'RenderCommandVisitor:dart',
'RenderEmbeddedTemplateCmd:dart', 'RenderEmbeddedTemplateCmd:dart',
'RenderEmbeddedTemplateCmd.attrNameAndValues',
'RenderEmbeddedTemplateCmd.children', 'RenderEmbeddedTemplateCmd.children',
'RenderEmbeddedTemplateCmd.children=', 'RenderEmbeddedTemplateCmd.eventTargetAndNames',
'RenderEmbeddedTemplateCmd.isBound',
'RenderEmbeddedTemplateCmd.isMerged', 'RenderEmbeddedTemplateCmd.isMerged',
'RenderEmbeddedTemplateCmd.isMerged=', 'RenderEmbeddedTemplateCmd.name',
'RenderEmbeddedTemplateCmd.ngContentIndex',
'RenderNgContentCmd:dart', 'RenderNgContentCmd:dart',
'RenderNgContentCmd.ngContentIndex', 'RenderNgContentCmd.ngContentIndex',
'RenderNgContentCmd.ngContentIndex=',
'RenderTemplateCmd:dart', 'RenderTemplateCmd:dart',
'RenderTextCmd:dart', 'RenderTextCmd:dart',
'RenderTextCmd.isBound',
'RenderTextCmd.ngContentIndex',
'RenderTextCmd.value', 'RenderTextCmd.value',
'RenderTextCmd.value=',
'RenderElementRef:dart', 'RenderElementRef:dart',
'RenderElementRef.boundElementIndex', 'RenderElementRef.boundElementIndex',
'RenderElementRef.boundElementIndex=', 'RenderElementRef.boundElementIndex=',
@ -1309,7 +1311,6 @@ var NG_ALL = [
'RenderElementRef.renderView=', 'RenderElementRef.renderView=',
'RenderEventDispatcher:dart', 'RenderEventDispatcher:dart',
'RenderNgContentCmd.index', 'RenderNgContentCmd.index',
'RenderNgContentCmd.index=',
'Stream:dart', 'Stream:dart',
'Stream.any():dart', 'Stream.any():dart',
'Stream.asBroadcastStream():dart', 'Stream.asBroadcastStream():dart',

View File

@ -35,6 +35,5 @@ TemplateCompiler createTemplateCompiler(AssetReader reader,
templateParser, templateParser,
new StyleCompiler(_xhr, _urlResolver), new StyleCompiler(_xhr, _urlResolver),
new CommandCompiler(), new CommandCompiler(),
cdCompiler, cdCompiler);
null /* appId */);
} }

View File

@ -176,12 +176,9 @@ class _CodegenState {
${_genDirectiveIndices()}; ${_genDirectiveIndices()};
static ${_genPrefix}ProtoChangeDetector static ${_genPrefix}ChangeDetector
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD( $CHANGE_DETECTOR_FACTORY_METHOD(a) {
${_genPrefix}ChangeDetectorDefinition def) { return new $_changeDetectorTypeName(a);
return new ${_genPrefix}PregenProtoChangeDetector(
(a) => new $_changeDetectorTypeName(a),
def);
} }
} }
'''); ''');
@ -575,7 +572,7 @@ class _CodegenState {
} }
} }
const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector'; const CHANGE_DETECTOR_FACTORY_METHOD = 'newChangeDetector';
const _BASE_CLASS = 'AbstractChangeDetector'; const _BASE_CLASS = 'AbstractChangeDetector';
const _CHANGES_LOCAL = 'changes'; const _CHANGES_LOCAL = 'changes';

View File

@ -4,7 +4,7 @@ environment:
dependencies: dependencies:
observe: '^0.13.1' observe: '^0.13.1'
dev_dependencies: dev_dependencies:
guinness: '^0.1.17' guinness: '^0.1.18'
intl: '^0.12.4' intl: '^0.12.4'
unittest: '^0.11.5+4' unittest: '^0.11.5+4'
quiver: '^0.21.4' quiver: '^0.21.4'