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'
stack_trace: '^1.1.1'
dev_dependencies:
guinness: '^0.1.17'
guinness: '^0.1.18'
transformers:
- angular2
- $dart2js:

View File

@ -63,7 +63,7 @@ export class ChangeDetectionCompiler {
var index = 0;
var sourceParts = changeDetectorDefinitions.map(definition => {
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
// suffix
// and have the same API for calling them!
@ -74,7 +74,7 @@ export class ChangeDetectionCompiler {
'dynamic' :
`${moduleRef(componentType.moduleUrl)}${componentType.name}`;
codegen.generate(typeRef, className, definition);
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
factories.push(`${className}.newChangeDetector`);
sourcePart = codegen.toString();
} else {
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 {
TemplateCmd,
text,
ngContent,
beginElement,
endElement,
beginComponent,
endComponent,
embeddedTemplate,
CompiledTemplate
TextCmd,
NgContentCmd,
BeginElementCmd,
EndElementCmd,
BeginComponentCmd,
EndComponentCmd,
EmbeddedTemplateCmd,
CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands';
import {
TemplateAst,
@ -32,12 +32,11 @@ import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {
shimHostAttribute,
shimContentAttribute,
shimContentAttributeExpr,
shimHostAttributeExpr
} from './style_compiler';
import {escapeSingleQuoteString, MODULE_SUFFIX} from './util';
escapeSingleQuoteString,
codeGenConstConstructorCall,
codeGenValueFn,
MODULE_SUFFIX
} from './util';
import {Injectable} from 'angular2/src/core/di';
export var TEMPLATE_COMMANDS_MODULE_REF =
@ -49,28 +48,24 @@ const STYLE_ATTR = 'style';
@Injectable()
export class CommandCompiler {
compileComponentRuntime(component: CompileDirectiveMetadata, appId: string, templateId: number,
template: TemplateAst[], changeDetectorFactories: Function[],
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
changeDetectorFactories: Function[],
componentTemplateFactory: Function): TemplateCmd[] {
var visitor = new CommandBuilderVisitor(
new RuntimeCommandFactory(component, appId, templateId, componentTemplateFactory,
changeDetectorFactories),
0);
new RuntimeCommandFactory(component, componentTemplateFactory, changeDetectorFactories), 0);
templateVisitAll(visitor, template);
return visitor.result;
}
compileComponentCodeGen(component: CompileDirectiveMetadata, appIdExpr: string,
templateIdExpr: string, template: TemplateAst[],
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
changeDetectorFactoryExpressions: string[],
componentTemplateFactory: Function): SourceExpression {
var visitor = new CommandBuilderVisitor(
new CodegenCommandFactory(component, appIdExpr, templateIdExpr, componentTemplateFactory,
changeDetectorFactoryExpressions),
0);
var visitor =
new CommandBuilderVisitor(new CodegenCommandFactory(component, componentTemplateFactory,
changeDetectorFactoryExpressions),
0);
templateVisitAll(visitor, template);
var source = `[${visitor.result.join(',')}]`;
return new SourceExpression([], source);
return new SourceExpression([], codeGenArray(visitor.result));
}
}
@ -83,7 +78,7 @@ interface CommandFactory<R> {
createEndElement(): R;
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): R;
encapsulation: ViewEncapsulation, ngContentIndex: number): R;
createEndComponent(): R;
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
@ -91,112 +86,89 @@ interface CommandFactory<R> {
}
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
constructor(private component: CompileDirectiveMetadata, private appId: string,
private templateId: number, private componentTemplateFactory: Function,
constructor(private component: CompileDirectiveMetadata,
private componentTemplateFactory: Function,
private changeDetectorFactories: Function[]) {}
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
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 {
return text(value, isBound, ngContentIndex);
return new TextCmd(value, isBound, ngContentIndex);
}
createNgContent(index: number, ngContentIndex: number): TemplateCmd {
return ngContent(index, ngContentIndex);
return new NgContentCmd(index, ngContentIndex);
}
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): TemplateCmd {
return beginElement(name, this._addStyleShimAttributes(attrNameAndValues, null, null),
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives),
isBound, ngContentIndex);
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
this._mapDirectives(directives), isBound, ngContentIndex);
}
createEndElement(): TemplateCmd { return endElement(); }
createEndElement(): TemplateCmd { return new EndElementCmd(); }
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
var nestedTemplate = this.componentTemplateFactory(directives[0]);
return beginComponent(
name, this._addStyleShimAttributes(attrNameAndValues, directives[0], nestedTemplate.id),
eventTargetAndNames, variableNameAndValues, this._mapDirectives(directives), nativeShadow,
ngContentIndex, nestedTemplate);
encapsulation: ViewEncapsulation, ngContentIndex: number): TemplateCmd {
var nestedTemplateAccessor = this.componentTemplateFactory(directives[0]);
return new BeginComponentCmd(name, attrNameAndValues, eventTargetAndNames,
variableNameAndValues, this._mapDirectives(directives),
encapsulation, ngContentIndex, nestedTemplateAccessor);
}
createEndComponent(): TemplateCmd { return endComponent(); }
createEndComponent(): TemplateCmd { return new EndComponentCmd(); }
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isMerged: boolean, ngContentIndex: number,
children: TemplateCmd[]): TemplateCmd {
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
this._mapDirectives(directives), isMerged, ngContentIndex,
this.changeDetectorFactories[embeddedTemplateIndex], children);
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues,
this._mapDirectives(directives), isMerged, ngContentIndex,
this.changeDetectorFactories[embeddedTemplateIndex], children);
}
}
class CodegenCommandFactory implements CommandFactory<string> {
constructor(private component: CompileDirectiveMetadata, private appIdExpr: string,
private templateIdExpr: string, private componentTemplateFactory: Function,
class CodegenCommandFactory implements CommandFactory<Expression> {
constructor(private component: CompileDirectiveMetadata,
private componentTemplateFactory: Function,
private changeDetectorFactoryExpressions: string[]) {}
private _addStyleShimAttributes(attrNameAndValues: string[],
localComponent: CompileDirectiveMetadata,
localTemplateIdExpr: string): any[] {
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);
createText(value: string, isBound: boolean, ngContentIndex: number): Expression {
return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'TextCmd')}(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`);
}
createText(value: string, isBound: boolean, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
}
createNgContent(index: number, ngContentIndex: number): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${index}, ${ngContentIndex})`;
createNgContent(index: number, ngContentIndex: number): Expression {
return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'NgContentCmd')}(${index}, ${ngContentIndex})`);
}
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isBound: boolean, ngContentIndex: number): string {
var attrsExpression = codeGenArray(this._addStyleShimAttributes(attrNameAndValues, null, null));
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`;
isBound: boolean, ngContentIndex: number): Expression {
var attrsExpression = codeGenArray(attrNameAndValues);
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[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
nativeShadow: boolean, ngContentIndex: number): string {
var nestedCompExpr = this.componentTemplateFactory(directives[0]);
var attrsExpression = codeGenArray(
this._addStyleShimAttributes(attrNameAndValues, directives[0], `${nestedCompExpr}.id`));
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${attrsExpression}, ${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${nativeShadow}, ${ngContentIndex}, ${nestedCompExpr})`;
encapsulation: ViewEncapsulation, ngContentIndex: number): Expression {
var attrsExpression = codeGenArray(attrNameAndValues);
return new Expression(
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginComponentCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
`${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[],
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
isMerged: boolean, ngContentIndex: number, children: string[]): string {
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`;
isMerged: boolean, ngContentIndex: number,
children: Expression[]): Expression {
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)) {
this.result.push(this.commandFactory.createBeginComponent(
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex));
component.template.encapsulation, ast.ngContentIndex));
templateVisitAll(this, ast.children);
this.result.push(this.commandFactory.createEndComponent());
} else {
@ -383,11 +355,21 @@ function escapeValue(value: 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 {
var expressions = directives.map(
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 {
escapeSingleQuoteString,
codeGenConcatArray,
codeGenMapArray,
codeGenReplaceAll,
codeGenExportVariable,
codeGenToString,
MODULE_SUFFIX
} from './util';
import {Injectable} from 'angular2/src/core/di';
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}`;
import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
@Injectable()
export class StyleCompiler {
@ -32,28 +23,16 @@ export class StyleCompiler {
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
compileComponentRuntime(appId: string, templateId: number,
template: CompileTemplateMetadata): Promise<string[]> {
compileComponentRuntime(template: CompileTemplateMetadata): Promise<Array<string | any[]>> {
var styles = template.styles;
var styleAbsUrls = template.styleUrls;
return this._loadStyles(styles, styleAbsUrls,
template.encapsulation === ViewEncapsulation.Emulated)
.then(styles => styles.map(style => StringWrapper.replaceAll(
style, COMPONENT_REGEX, componentId(appId, templateId))));
template.encapsulation === ViewEncapsulation.Emulated);
}
compileComponentCodeGen(appIdExpression: string, templateIdExpression: string,
template: CompileTemplateMetadata): SourceExpression {
compileComponentCodeGen(template: CompileTemplateMetadata): SourceExpression {
var shim = template.encapsulation === ViewEncapsulation.Emulated;
var suffix;
if (shim) {
suffix = codeGenMapArray(
['style'],
`style${codeGenReplaceAll(COMPONENT_VARIABLE, componentIdExpression(appIdExpression, templateIdExpression))}`);
} else {
suffix = '';
}
return this._styleCodeGen(template.styles, template.styleUrls, shim, suffix);
return this._styleCodeGen(template.styles, template.styleUrls, shim);
}
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
@ -61,17 +40,16 @@ export class StyleCompiler {
return [
this._styleModule(
stylesheetUrl, false,
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false, '')),
this._styleModule(
stylesheetUrl, true,
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, true, ''))
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false)),
this._styleModule(stylesheetUrl, true, this._styleCodeGen([styleWithImports.style],
styleWithImports.styleUrls, true))
];
}
clearCache() { this._styleCache.clear(); }
private _loadStyles(plainStyles: string[], absUrls: string[],
encapsulate: boolean): Promise<string[]> {
encapsulate: boolean): Promise<Array<string | any[]>> {
var promises = absUrls.map((absUrl) => {
var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`;
var result = this._styleCache.get(cacheKey);
@ -86,22 +64,23 @@ export class StyleCompiler {
return result;
});
return PromiseWrapper.all(promises).then((nestedStyles: string[][]) => {
var result = plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
nestedStyles.forEach(styles => styles.forEach(style => result.push(style)));
var result: Array<string | any[]> =
plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
nestedStyles.forEach(styles => result.push(styles));
return result;
});
}
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean,
suffix: string): SourceExpression {
var expressionSource = `(`;
expressionSource +=
`[${plainStyles.map( plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean): SourceExpression {
var arrayPrefix = IS_DART ? `const` : '';
var styleExpressions = plainStyles.map(
plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)));
for (var i = 0; i < absUrls.length; i++) {
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);
}
@ -122,29 +101,3 @@ export class StyleCompiler {
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 {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {
CompiledTemplate,
CompiledComponentTemplate,
TemplateCmd,
nextTemplateId,
CompiledHostTemplate
CompiledHostTemplate,
BeginComponentCmd
} from 'angular2/src/core/linker/template_commands';
import {
createHostComponentMeta,
@ -23,7 +23,6 @@ import {CommandCompiler} from './command_compiler';
import {TemplateParser} from './template_parser';
import {TemplateNormalizer} from './template_normalizer';
import {RuntimeMetadataResolver} from './runtime_metadata';
import {APP_ID} from 'angular2/src/core/application_tokens';
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
import {
@ -32,22 +31,19 @@ import {
codeGenValueFn,
MODULE_SUFFIX
} from './util';
import {Inject} from 'angular2/src/core/di';
@Injectable()
export class TemplateCompiler {
private _hostCacheKeys = new Map<Type, any>();
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
private _appId: string;
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
private _nextTemplateId: number = 0;
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
private _templateNormalizer: TemplateNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _commandCompiler: CommandCompiler,
private _cdCompiler: ChangeDetectionCompiler, @Inject(APP_ID) appId: string) {
this._appId = appId;
}
private _cdCompiler: ChangeDetectionCompiler) {}
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise<CompileDirectiveMetadata> {
@ -87,7 +83,7 @@ export class TemplateCompiler {
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
}
return this._compiledTemplateDone.get(hostCacheKey)
.then(compiledTemplate => new CompiledHostTemplate(() => compiledTemplate));
.then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
}
clearCache() {
@ -97,57 +93,55 @@ export class TemplateCompiler {
this._compiledTemplateDone.clear();
}
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
viewDirectives: CompileDirectiveMetadata[],
compilingComponentCacheKeys: Set<any>): CompiledTemplate {
private _compileComponentRuntime(
cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
compilingComponentCacheKeys: Set<any>): CompiledComponentTemplate {
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
var done = this._compiledTemplateDone.get(cacheKey);
if (isBlank(compiledTemplate)) {
var styles;
var styles = [];
var changeDetectorFactory;
var commands;
var templateId = nextTemplateId();
compiledTemplate =
new CompiledTemplate(templateId, (_a, _b) => [changeDetectorFactory, commands, styles]);
var commands = [];
var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
compiledTemplate = new CompiledComponentTemplate(
templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
compilingComponentCacheKeys.add(cacheKey);
done =
PromiseWrapper.all([
<any>this._styleCompiler.compileComponentRuntime(this._appId, templateId,
compMeta.template)
].concat(viewDirectives.map(dirMeta =>
this.normalizeDirectiveMetadata(dirMeta))))
.then((stylesAndNormalizedViewDirMetas: any[]) => {
var childPromises = [];
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
var parsedTemplate = this._templateParser.parse(
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
done = PromiseWrapper
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
viewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
.then((stylesAndNormalizedViewDirMetas: any[]) => {
var childPromises = [];
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
var parsedTemplate = this._templateParser.parse(
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
compMeta.type, compMeta.changeDetection, parsedTemplate);
changeDetectorFactory = changeDetectorFactories[0];
styles = stylesAndNormalizedViewDirMetas[0];
commands = this._compileCommandsRuntime(compMeta, templateId, parsedTemplate,
changeDetectorFactories,
compilingComponentCacheKeys, childPromises);
return PromiseWrapper.all(childPromises);
})
.then((_) => {
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
return compiledTemplate;
});
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
compMeta.type, compMeta.changeDetection, parsedTemplate);
changeDetectorFactory = changeDetectorFactories[0];
var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
tmpStyles.forEach(style => styles.push(style));
var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
compMeta, parsedTemplate, changeDetectorFactories,
compilingComponentCacheKeys, childPromises);
tmpCommands.forEach(cmd => commands.push(cmd));
return PromiseWrapper.all(childPromises);
})
.then((_) => {
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
return compiledTemplate;
});
this._compiledTemplateDone.set(cacheKey, done);
}
return compiledTemplate;
}
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, templateId: number,
parsedTemplate: TemplateAst[],
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
changeDetectorFactories: Function[],
compilingComponentCacheKeys: Set<Type>,
childPromises: Promise<any>[]): TemplateCmd[] {
return this._commandCompiler.compileComponentRuntime(
compMeta, this._appId, templateId, parsedTemplate, changeDetectorFactories,
var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
compMeta, parsedTemplate, changeDetectorFactories,
(childComponentDir: CompileDirectiveMetadata) => {
var childCacheKey = childComponentDir.type.runtime;
var childViewDirectives: CompileDirectiveMetadata[] =
@ -160,8 +154,14 @@ export class TemplateCompiler {
// Only wait for a child if it is not a cycle
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 {
@ -171,40 +171,35 @@ export class TemplateCompiler {
var declarations = [];
var templateArguments = [];
var componentMetas: CompileDirectiveMetadata[] = [];
var templateIdVariable = 'templateId';
var appIdVariable = 'appId';
components.forEach(componentWithDirs => {
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
assertComponent(compMeta);
componentMetas.push(compMeta);
this._processTemplateCodeGen(compMeta, appIdVariable, templateIdVariable,
this._processTemplateCodeGen(compMeta,
<CompileDirectiveMetadata[]>componentWithDirs.directives,
declarations, templateArguments);
if (compMeta.dynamicLoadable) {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
componentMetas.push(hostMeta);
this._processTemplateCodeGen(hostMeta, appIdVariable, templateIdVariable, [compMeta],
declarations, templateArguments);
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
}
});
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
index: number) => {
var templateDataFn = codeGenValueFn([appIdVariable, templateIdVariable],
`[${(<any[]>templateArguments[index]).join(',')}]`);
var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
var constructionKeyword = IS_DART ? 'const' : 'new';
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;
if (compMeta.type.isHost) {
var factoryName = `_hostTemplateFactory${index}`;
declarations.push(`${codeGenValueFn([], compiledTemplateExpr, factoryName)};`);
var constructionKeyword = IS_DART ? 'const' : 'new';
variableValueExpr =
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${factoryName})`;
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
} else {
variableValueExpr = compiledTemplateExpr;
}
declarations.push(
`${codeGenExportVariable(templateVariableName(compMeta.type), compMeta.type.isHost)}${variableValueExpr};`);
var varName = templateVariableName(compMeta.type);
declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
});
var moduleUrl = components[0].component.type.moduleUrl;
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
@ -214,17 +209,16 @@ export class TemplateCompiler {
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
}
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata, appIdExpr: string,
templateIdExpr: string, directives: CompileDirectiveMetadata[],
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
directives: CompileDirectiveMetadata[],
targetDeclarations: string[], targetTemplateArguments: any[][]) {
var styleExpr =
this._styleCompiler.compileComponentCodeGen(appIdExpr, templateIdExpr, compMeta.template);
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
var parsedTemplate =
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
compMeta.type, compMeta.changeDetection, parsedTemplate);
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
compMeta, appIdExpr, templateIdExpr, parsedTemplate, changeDetectorsExprs.expressions,
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
codeGenComponentTemplateFactory);
addAll(styleExpr.declarations, targetDeclarations);
@ -251,6 +245,10 @@ function templateVariableName(type: CompileTypeMetadata): string {
return `${type.name}Template`;
}
function templateGetterName(type: CompileTypeMetadata): string {
return `${templateVariableName(type)}Getter`;
}
function templateModuleUrl(moduleUrl: string): string {
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
@ -263,5 +261,5 @@ function addAll(source: any[], target: any[]) {
}
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) {
return isConst ? `const ${name} = ` : `final ${name} = `;
return `const ${name} = `;
} else {
return `var ${name} = exports['${name}'] = `;
}
}
export function codeGenConcatArray(expression: string): string {
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
}
export function codeGenMapArray(argNames: string[], callback: string): string {
export function codeGenConstConstructorCall(name: string): string {
if (IS_DART) {
return `.map( (${argNames.join(',')}) => ${callback} ).toList()`;
return `const ${name}`;
} else {
return `.map(function(${argNames.join(',')}) { return ${callback}; })`;
}
}
export function codeGenReplaceAll(pattern: string, expression: string): string {
if (IS_DART) {
return `.replaceAll('${pattern}', ${expression})`;
} else {
return `.replace(/${pattern}/g, ${expression})`;
return `new ${name}`;
}
}

View File

@ -1,6 +1,6 @@
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';
@ -13,12 +13,12 @@ import {ProtoElementInjector, DirectiveProvider} from './element_injector';
import {DirectiveResolver} from './directive_resolver';
import {ViewResolver} from './view_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 {
visitAllCommands,
CompiledTemplate,
CompiledComponentTemplate,
CompiledHostTemplate,
TemplateCmd,
CommandVisitor,
@ -36,7 +36,9 @@ import {APP_ID} from 'angular2/src/core/application_tokens';
@Injectable()
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,
@Optional() @Inject(AMBIENT_PIPES) private _ambientPipes: Array<Type | any[]>,
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
@ -45,13 +47,16 @@ export class ProtoViewFactory {
clearCache() { this._cache.clear(); }
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
var compiledTemplate = compiledHostTemplate.getTemplate();
var compiledTemplate = compiledHostTemplate.template;
var result = this._cache.get(compiledTemplate.id);
if (isBlank(result)) {
var templateData = compiledTemplate.getData(this._appId);
var emptyMap: {[key: string]: PipeProvider} = {};
result = new AppProtoView(templateData.commands, ViewType.HOST, true,
templateData.changeDetectorFactory, null, new ProtoPipes(emptyMap));
var shortId = `${this._appId}-${this._nextTemplateId++}`;
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);
}
return result;
@ -62,18 +67,19 @@ export class ProtoViewFactory {
if (isBlank(nestedProtoView)) {
var component = cmd.directives[0];
var view = this._viewResolver.resolve(component);
var compiledTemplateData = cmd.template.getData(this._appId);
this._renderer.registerComponentTemplate(cmd.templateId, compiledTemplateData.commands,
compiledTemplateData.styles, cmd.nativeShadow);
var compiledTemplate = cmd.templateGetter();
var styles = _flattenStyleArr(compiledTemplate.styles, []);
var shortId = `${this._appId}-${this._nextTemplateId++}`;
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
nestedProtoView = new AppProtoView(compiledTemplateData.commands, ViewType.COMPONENT, true,
compiledTemplateData.changeDetectorFactory, null,
ProtoPipes.fromProviders(boundPipes));
nestedProtoView = new AppProtoView(
compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
// Note: The cache is updated before recursing
// to be able to resolve cycles
this._cache.set(cmd.template.id, nestedProtoView);
this._cache.set(compiledTemplate.id, nestedProtoView);
this._initializeProtoView(nestedProtoView, null);
}
return nestedProtoView;
@ -81,7 +87,7 @@ export class ProtoViewFactory {
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): 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));
if (cmd.isMerged) {
this.initializeProtoViewIfNeeded(nestedProtoView);
@ -91,7 +97,7 @@ export class ProtoViewFactory {
initializeProtoViewIfNeeded(protoView: AppProtoView) {
if (!protoView.isInitialized()) {
var render = this._renderer.createProtoView(protoView.templateCmds);
var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
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 {unimplemented} from 'angular2/src/facade/exceptions';
import {
RenderTemplateCmd,
RenderCommandVisitor,
@ -8,12 +9,10 @@ import {
RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/render';
var _nextTemplateId: number = 0;
export function nextTemplateId(): number {
return _nextTemplateId++;
}
import {ViewEncapsulation} from 'angular2/src/core/metadata';
// Export ViewEncapsulation so that compiled templates only need to depend
// on template_commands.
export {ViewEncapsulation} from 'angular2/src/core/metadata';
/**
* A compiled host template.
@ -23,34 +22,16 @@ export function nextTemplateId(): number {
*/
@CONST()
export class CompiledHostTemplate {
// Note: _templateGetter is a function so that CompiledHostTemplate can be
// a const!
constructor(private _templateGetter: Function) {}
getTemplate(): CompiledTemplate { return this._templateGetter(); }
constructor(public template: CompiledComponentTemplate) {}
}
/**
* A compiled template.
*/
export class CompiledTemplate {
// Note: paramGetter is a function so that we can have cycles between templates!
// paramGetter returns a tuple with:
// - ChangeDetector factory function
// - 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()
export class CompiledComponentTemplate {
constructor(public id: string, public changeDetectorFactory: Function,
public commands: TemplateCmd[], public styles: string[]) {}
}
const EMPTY_ARR = CONST_EXPR([]);
@ -59,6 +40,7 @@ export interface TemplateCmd extends RenderTemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any;
}
@CONST()
export class TextCmd implements TemplateCmd, RenderTextCmd {
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
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 {
return new TextCmd(value, isBound, ngContentIndex);
}
@CONST()
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
isBound: boolean = false;
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 {
return new NgContentCmd(index, ngContentIndex);
}
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
variableNameAndValues: Array<string | number>;
eventTargetAndNames: string[];
directives: Type[];
visit(visitor: RenderCommandVisitor, context: any): any;
export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
get variableNameAndValues(): Array<string | number> { return unimplemented(); }
get eventTargetAndNames(): string[] { return unimplemented(); }
get directives(): Type[] { return unimplemented(); }
abstract visit(visitor: RenderCommandVisitor, context: any): any;
}
@CONST()
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
constructor(public name: string, public attrNameAndValues: 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 {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndElement(context);
}
}
export function endElement(): TemplateCmd {
return new EndElementCmd();
}
@CONST()
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
isBound: boolean = true;
templateId: number;
constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[],
public nativeShadow: boolean, public ngContentIndex: number,
public template: CompiledTemplate) {
this.templateId = template.id;
}
public encapsulation: ViewEncapsulation, public ngContentIndex: number,
// Note: the template needs to be stored as a function
// so that we can resolve cycles
public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
get templateId(): string { return this.templateGetter().id; }
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context);
}
}
export function beginComponent(
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);
}
@CONST()
export class EndComponentCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndComponent(context);
}
}
export function endComponent(): TemplateCmd {
return new EndComponentCmd();
}
@CONST()
export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
RenderEmbeddedTemplateCmd {
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 {
visitText(cmd: TextCmd, context: any): any;

View File

@ -318,8 +318,8 @@ export class AppProtoView {
textBindingCount = null;
render: renderApi.RenderProtoViewRef = null;
constructor(public templateCmds: TemplateCmd[], public type: ViewType, public isMergable: boolean,
public changeDetectorFactory: Function,
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
public isMergable: boolean, public changeDetectorFactory: Function,
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
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 {ViewEncapsulation} from 'angular2/src/core/metadata';
/**
* Represents an Angular ProtoView in the Rendering Context.
@ -69,37 +71,40 @@ export class RenderFragmentRef {}
// TODO(i): refactor into an interface
export class RenderViewRef {}
export interface RenderTemplateCmd { visit(visitor: RenderCommandVisitor, context: any): any; }
export interface RenderBeginCmd extends RenderTemplateCmd {
ngContentIndex: number;
isBound: boolean;
export abstract class RenderTemplateCmd {
abstract visit(visitor: RenderCommandVisitor, context: any): any;
}
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
index: number;
get index(): number { return unimplemented(); };
// The index of the NgContent element into which this
// NgContent element should be projected (if any)
ngContentIndex: number;
get ngContentIndex(): number { return unimplemented(); };
}
export interface RenderBeginElementCmd extends RenderBeginCmd {
name: string;
attrNameAndValues: string[];
eventTargetAndNames: string[];
export abstract class RenderBeginElementCmd extends RenderBeginCmd {
get name(): string { return unimplemented(); };
get attrNameAndValues(): string[] { return unimplemented(); };
get eventTargetAndNames(): string[] { return unimplemented(); };
}
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
nativeShadow: boolean;
templateId: number;
export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
get templateId(): string { return unimplemented(); };
}
export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
isMerged: boolean;
children: RenderTemplateCmd[];
export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
get isMerged(): boolean { return unimplemented(); };
get children(): RenderTemplateCmd[] { return unimplemented(); };
}
export interface RenderCommandVisitor {
@ -156,6 +161,10 @@ export interface RenderElementRef {
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.
@ -177,13 +186,13 @@ export abstract class Renderer {
* Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
* {@link #createProtoView creating Render ProtoView}.
*/
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[],
styles: string[], nativeShadow: boolean);
abstract registerComponentTemplate(template: RenderComponentTemplate);
/**
* 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`.

View File

@ -1,6 +1,13 @@
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
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 {DOM} from 'angular2/src/core/dom/dom_adapter';
@ -18,13 +25,15 @@ import {
RenderFragmentRef,
RenderViewWithFragments,
RenderTemplateCmd,
RenderEventDispatcher
RenderEventDispatcher,
RenderComponentTemplate
} from '../api';
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 {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
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});
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
abstract registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[],
styles: string[], nativeShadow: boolean);
abstract registerComponentTemplate(template: RenderComponentTemplate);
abstract resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
abstract resolveComponentTemplate(templateId: string): RenderComponentTemplate;
createProtoView(cmds: RenderTemplateCmd[]): RenderProtoViewRef {
return new DefaultProtoViewRef(cmds);
}
abstract createProtoView(componentTemplateId: string,
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
hostElementSelector: string): RenderViewWithFragments;
@ -189,7 +196,7 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
}
abstract createElement(name: string, attrNameAndValues: string[]): Node;
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 : ''); }
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
abstract on(element: Node, eventName: string, callback: Function);
@ -252,8 +259,8 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
@Injectable()
export class DomRenderer_ extends DomRenderer {
private _componentCmds: Map<number, RenderTemplateCmd[]> = new Map<number, RenderTemplateCmd[]>();
private _nativeShadowStyles: Map<number, string[]> = new Map<number, string[]>();
private _componentTpls: Map<string, RenderComponentTemplate> =
new Map<string, RenderComponentTemplate>();
private _document;
constructor(private _eventManager: EventManager,
@ -263,18 +270,20 @@ export class DomRenderer_ extends DomRenderer {
this._document = document;
}
registerComponentTemplate(templateId: number, commands: RenderTemplateCmd[], styles: string[],
nativeShadow: boolean) {
this._componentCmds.set(templateId, commands);
if (nativeShadow) {
this._nativeShadowStyles.set(templateId, styles);
} else {
this._domSharedStylesHost.addStyles(styles);
registerComponentTemplate(template: RenderComponentTemplate) {
this._componentTpls.set(template.id, template);
if (template.encapsulation !== ViewEncapsulation.Native) {
var encapsulatedStyles = encapsulateStyles(template);
this._domSharedStylesHost.addStyles(encapsulatedStyles);
}
}
resolveComponentTemplate(templateId: number): RenderTemplateCmd[] {
return this._componentCmds.get(templateId);
createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
return new DefaultProtoViewRef(this._componentTpls.get(componentTemplateId), cmds);
}
resolveComponentTemplate(templateId: string): RenderComponentTemplate {
return this._componentTpls.get(templateId);
}
/** @internal */
@ -299,7 +308,8 @@ export class DomRenderer_ extends DomRenderer {
private _createView(protoViewRef: RenderProtoViewRef,
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;
for (var i = 0; i < sdRoots.length; i++) {
this._domSharedStylesHost.addHost(sdRoots[i]);
@ -375,11 +385,11 @@ export class DomRenderer_ extends DomRenderer {
createRootContentInsertionPoint(): Node {
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 styles = this._nativeShadowStyles.get(templateId);
for (var i = 0; i < styles.length; i++) {
DOM.appendChild(sr, DOM.createStyleElement(styles[i]));
var tpl = this._componentTpls.get(templateId);
for (var i = 0; i < tpl.styles.length; i++) {
DOM.appendChild(sr, DOM.createStyleElement(tpl.styles[i]));
}
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 {
RenderComponentTemplate,
RenderViewRef,
RenderEventDispatcher,
RenderTemplateCmd,
@ -11,7 +12,9 @@ import {
} from './api';
export class DefaultProtoViewRef extends RenderProtoViewRef {
constructor(public cmds: RenderTemplateCmd[]) { super(); }
constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
super();
}
}
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 {
RenderEventDispatcher,
RenderTemplateCmd,
@ -7,17 +7,34 @@ import {
RenderBeginComponentCmd,
RenderNgContentCmd,
RenderTextCmd,
RenderEmbeddedTemplateCmd
RenderEmbeddedTemplateCmd,
RenderComponentTemplate
} from './api';
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> {
var view: DefaultRenderView<any>;
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
view.dispatchRenderEvent(boundElementIndex, eventName, event);
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
context.build(fragmentCmds);
context.build(componentTemplate, cmds);
var fragments: DefaultRenderFragmentRef<any>[] = [];
for (var i = 0; i < context.fragments.length; i++) {
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
@ -29,12 +46,12 @@ export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceEleme
}
export interface NodeFactory<N> {
resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
resolveComponentTemplate(templateId: string): RenderComponentTemplate;
createTemplateAnchor(attrNameAndValues: string[]): N;
createElement(name: string, attrNameAndValues: string[]): N;
createRootContentInsertionPoint(): N;
mergeElement(existing: N, attrNameAndValues: string[]);
createShadowRoot(host: N, templateId: number): N;
createShadowRoot(host: N, templateId: string): N;
createText(value: string): N;
appendChild(parent: N, child: N);
on(element: N, eventName: string, callback: Function);
@ -57,8 +74,8 @@ class BuildContext<N> {
componentCount: number = 0;
isHost: boolean;
build(fragmentCmds: RenderTemplateCmd[]) {
this.enqueueFragmentBuilder(null, fragmentCmds);
build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
this.enqueueRootBuilder(template, cmds);
this._build(this._builders[0]);
}
@ -73,14 +90,22 @@ class BuildContext<N> {
enqueueComponentBuilder(component: Component<N>) {
this.componentCount++;
this._builders.push(new RenderViewBuilder<N>(
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId)));
this._builders.push(
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 = [];
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 {
@ -116,14 +141,15 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
parentStack: Array<N | Component<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;
this.parentStack = [rootNodesParent];
}
build(context: BuildContext<N>) {
for (var i = 0; i < this.commands.length; i++) {
this.commands[i].visit(this, context);
var cmds = this.cmds;
for (var i = 0; i < cmds.length; i++) {
cmds[i].visit(this, context);
}
}
@ -158,7 +184,7 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
return null;
}
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
this.parentStack.push(this._beginElement(cmd, context));
this.parentStack.push(this._beginElement(cmd, context, null));
return null;
}
visitEndElement(context: BuildContext<N>): any {
@ -166,14 +192,17 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
return null;
}
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;
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);
}
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);
this.parentStack.push(component);
return null;
@ -187,18 +216,34 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
this._addChild(el, cmd.ngContentIndex, context);
context.boundElements.push(el);
if (cmd.isMerged) {
context.enqueueFragmentBuilder(this.parentComponent, cmd.children);
context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
}
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 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)) {
context.factory.mergeElement(el, cmd.attrNameAndValues);
context.factory.mergeElement(el, attrNameAndValues);
this.fragmentRootNodes.push(el);
} else {
el = context.factory.createElement(cmd.name, cmd.attrNameAndValues);
el = context.factory.createElement(cmd.name, attrNameAndValues);
this._addChild(el, cmd.ngContentIndex, context);
}
if (cmd.isBound) {
@ -232,11 +277,11 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
class Component<N> {
private contentNodesByNgContentIndex: N[][] = [];
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd,
public isRoot: boolean) {}
constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
public template: RenderComponentTemplate) {}
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
if (isBlank(ngContentIndex)) {
if (this.cmd.nativeShadow) {
if (this.template.encapsulation === ViewEncapsulation.Native) {
context.factory.appendChild(this.hostElement, node);
}
} 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 {
constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
public attrNameAndValues: string[], public eventTargetAndNames: string[],
public nativeShadow: boolean, public templateId: number) {}
public templateId: string) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +1,4 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
// 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
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 {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 {evalModule} from './eval_module';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
@ -42,8 +42,6 @@ var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
export function main() {
describe('StyleCompiler', () => {
var xhr: SpyXHR;
var templateId;
var appId;
beforeEachBindings(() => {
xhr = <any>new SpyXHR();
@ -52,11 +50,7 @@ export function main() {
var compiler: StyleCompiler;
beforeEach(inject([StyleCompiler], (_compiler) => {
templateId = 23;
appId = 'app1';
compiler = _compiler;
}));
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
describe('compileComponentRuntime', () => {
var xhrUrlResults;
@ -82,10 +76,8 @@ export function main() {
}
return PromiseWrapper.resolve(response);
});
return compiler.compileComponentRuntime(
appId, templateId,
new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
return compiler.compileComponentRuntime(new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
}
describe('no shim', () => {
@ -102,7 +94,7 @@ export function main() {
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done();
});
}));
@ -111,7 +103,7 @@ export function main() {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation)
.then(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();
});
}));
@ -124,8 +116,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]);
async.done();
});
@ -135,8 +127,8 @@ export function main() {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {color: blue}'
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
['span[_ngcontent-%COMP%] {color: blue}']
]);
async.done();
});
@ -147,9 +139,11 @@ export function main() {
encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'a[_ngcontent-app1-23] {color: green}',
'span[_ngcontent-app1-23] {color: blue}'
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
[
'a[_ngcontent-%COMP%] {color: green}',
['span[_ngcontent-%COMP%] {color: blue}']
]
]);
async.done();
});
@ -162,8 +156,8 @@ export function main() {
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
])
.then((styleArrays) => {
expect(styleArrays[0]).toEqual(['span {color: blue}']);
expect(styleArrays[1]).toEqual(['span {color: blue}']);
expect(styleArrays[0]).toEqual([['span {color: blue}']]);
expect(styleArrays[1]).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1);
async.done();
});
@ -175,8 +169,8 @@ export function main() {
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((styles1) => {
expect(styles0).toEqual(['span {color: blue}']);
expect(styles1).toEqual(['span {color: blue}']);
expect(styles0).toEqual([['span {color: blue}']]);
expect(styles1).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1);
async.done();
});
@ -192,7 +186,7 @@ export function main() {
})
.then((styles) => {
expect(xhrCount).toBe(2);
expect(styles).toEqual(['span {color: black}']);
expect(styles).toEqual([['span {color: black}']]);
async.done();
});
}));
@ -201,10 +195,8 @@ export function main() {
describe('compileComponentCodeGen', () => {
function compile(styles: string[], styleAbsUrls: string[],
encapsulation: ViewEncapsulation): Promise<string[]> {
var sourceExpression = compiler.compileComponentCodeGen(
`'${appId}'`, `${templateId}`,
new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
var sourceExpression = compiler.compileComponentCodeGen(new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
};
@ -232,7 +224,7 @@ export function main() {
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done();
});
}), 1000);
@ -245,8 +237,8 @@ export function main() {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]);
async.done();
});
@ -256,8 +248,8 @@ export function main() {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {color: red}',
'span[_ngcontent-app1-23] {\ncolor: blue;\n}'
'div[_ngcontent-%COMP%] {color: red}',
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
]);
async.done();
});
@ -289,10 +281,10 @@ export function main() {
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
.then(stylesAndShimStyles => {
var expected = [
['div {color: red}', 'span {color: blue}'],
['div {color: red}', ['span {color: blue}']],
[
'div[_ngcontent-%COMP%] {color: red}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
]
];
compareStyles(stylesAndShimStyles[0], expected[0]);
@ -307,20 +299,27 @@ export function main() {
function testableExpression(source: SourceExpression): SourceModule {
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], source.expression)};`;
${codeGenValueFn(['_'], source.expression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(null, testableSource);
}
function testableModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// 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);
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,
TemplateCmd,
visitAllCommands,
CompiledTemplate
CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands';
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 {TestDispatcher, TestPipes} from './change_detector_mocks';
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!
const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
const APP_ID_VALUE = 'app1';
export function main() {
describe('TemplateCompiler', () => {
var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachBindings(() => [provide(APP_ID, {useValue: APP_ID_VALUE}), TEST_PROVIDERS]);
beforeEachBindings(() => TEST_PROVIDERS);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => {
compiler = _compiler;
@ -127,10 +124,10 @@ export function main() {
}));
}
describe('compileHostComponentRuntime', () => {
xdescribe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0])
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.getTemplate()));
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.template));
}
runTests(compile);
@ -315,38 +312,39 @@ class NonComponent {
function testableTemplateModule(sourceModule: SourceModule,
normComp: CompileDirectiveMetadata): SourceModule {
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}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// Attention: read by eval!
export function humanizeTemplate(
template: CompiledTemplate,
humanizedTemplates: Map<number, {[key: string]: any}> = null): {[key: string]: any} {
template: CompiledComponentTemplate,
humanizedTemplates: Map<string, {[key: string]: any}> = null): {[key: string]: any} {
if (isBlank(humanizedTemplates)) {
humanizedTemplates = new Map<number, {[key: string]: any}>();
humanizedTemplates = new Map<string, {[key: string]: any}>();
}
var result = humanizedTemplates.get(template.id);
if (isPresent(result)) {
return result;
}
var templateData = template.getData(APP_ID_VALUE);
var commands = [];
result = {
'styles': templateData.styles,
'styles': template.styles,
'commands': commands,
'cd': testChangeDetector(templateData.changeDetectorFactory)
'cd': testChangeDetector(template.changeDetectorFactory)
};
humanizedTemplates.set(template.id, result);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), templateData.commands);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
return result;
}
@ -373,7 +371,7 @@ function testChangeDetector(changeDetectorFactory: Function): string[] {
class CommandHumanizer implements CommandVisitor {
constructor(private result: any[],
private humanizedTemplates: Map<number, {[key: string]: any}>) {}
private humanizedTemplates: Map<string, {[key: string]: any}>) {}
visitText(cmd: TextCmd, context: any): any {
this.result.push(`#text(${cmd.value})`);
return null;
@ -389,7 +387,7 @@ class CommandHumanizer implements CommandVisitor {
}
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push(`<${cmd.name}>`);
this.result.push(humanizeTemplate(cmd.template, this.humanizedTemplates));
this.result.push(humanizeTemplate(cmd.templateGetter(), this.humanizedTemplates));
return null;
}
visitEndComponent(context: any): any { return this.visitEndElement(context); }

View File

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

View File

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

View File

@ -17,7 +17,7 @@ import {Component, View, provide} from 'angular2/core';
import {SpyProtoViewFactory} from '../spies';
import {
CompiledHostTemplate,
CompiledTemplate,
CompiledComponentTemplate,
BeginComponentCmd
} from 'angular2/src/core/linker/template_commands';
import {Compiler} from 'angular2/src/core/linker/compiler';
@ -35,7 +35,7 @@ export function main() {
beforeEachBindings(() => {
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);
var factory = provide(ProtoViewFactory, {useValue: protoViewFactorySpy});
var classProvider = provide(Compiler, {useClass: Compiler_});
@ -45,7 +45,7 @@ export function main() {
beforeEach(inject([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]));
}));

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',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MainComp, new ViewMetadata({

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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