refactor(compiler): cleanup and preparation for integration
- Rename `DirectiveMetadata` into `CompileDirectiveMetadata`, merge with `NormalizedDirectiveMetadata` and remove `ChangeDetectionMetadata` - Store change detector factories not as array but directly at the `CompiledTemplate` or the embedded template to make instantiation easier later on - Already analyze variable values and map them to `Directive.exportAs` - Keep the directive sort order as specified in the `@View()` annotation - Allow to clear the runtime cache in `StyleCompiler` and `TemplateCompiler` - Ignore `script` elements to match the semantics of the current compiler - Make all components dynamically loadable and remove the previously introduced property `@Component#dynamicLoadable` for now until we find a better option to configure this - Don’t allow to specify bindings in `@View#directives` and `@View#pipes` as this was never supported by the transformer (see below for the breaking change) BREAKING CHANGE: - don't support DI bindings in `@View#directives` and `@View@pipes` any more in preparation of integrating the new compiler. Use `@Directive#bindings` to reexport directives under a different token instead. Part of #3605 Closes #4314
This commit is contained in:
parent
eb7839e0ec
commit
cc0c30484f
@ -14,7 +14,7 @@ import {
|
|||||||
ASTWithSource
|
ASTWithSource
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
import {NormalizedDirectiveMetadata, TypeMetadata} from './directive_metadata';
|
import {CompileDirectiveMetadata, CompileTypeMetadata} from './directive_metadata';
|
||||||
import {
|
import {
|
||||||
TemplateAst,
|
TemplateAst,
|
||||||
ElementAst,
|
ElementAst,
|
||||||
@ -32,9 +32,10 @@ import {
|
|||||||
AttrAst,
|
AttrAst,
|
||||||
TextAst
|
TextAst
|
||||||
} from './template_ast';
|
} from './template_ast';
|
||||||
|
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||||
|
|
||||||
export function createChangeDetectorDefinitions(
|
export function createChangeDetectorDefinitions(
|
||||||
componentType: TypeMetadata, componentStrategy: ChangeDetectionStrategy,
|
componentType: CompileTypeMetadata, componentStrategy: ChangeDetectionStrategy,
|
||||||
genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] {
|
genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] {
|
||||||
var pvVisitors = [];
|
var pvVisitors = [];
|
||||||
var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy);
|
var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy);
|
||||||
@ -59,7 +60,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||||
this.boundElementCount++;
|
this.boundElementCount++;
|
||||||
templateVisitAll(this, ast.directives);
|
for (var i = 0; i < ast.directives.length; i++) {
|
||||||
|
ast.directives[i].visit(this, i);
|
||||||
|
}
|
||||||
|
|
||||||
var childVisitor =
|
var childVisitor =
|
||||||
new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default);
|
new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default);
|
||||||
@ -76,7 +79,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
templateVisitAll(this, ast.properties, null);
|
templateVisitAll(this, ast.properties, null);
|
||||||
templateVisitAll(this, ast.events);
|
templateVisitAll(this, ast.events);
|
||||||
templateVisitAll(this, ast.vars);
|
templateVisitAll(this, ast.exportAsVars);
|
||||||
for (var i = 0; i < ast.directives.length; i++) {
|
for (var i = 0; i < ast.directives.length; i++) {
|
||||||
ast.directives[i].visit(this, i);
|
ast.directives[i].visit(this, i);
|
||||||
}
|
}
|
||||||
@ -94,8 +97,8 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any {
|
visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any {
|
||||||
var bindingRecord =
|
var bindingRecord =
|
||||||
isPresent(directiveRecord) ?
|
isPresent(directiveRecord) ?
|
||||||
BindingRecord.createForHostEvent(ast.handler, ast.name, directiveRecord) :
|
BindingRecord.createForHostEvent(ast.handler, ast.fullName, directiveRecord) :
|
||||||
BindingRecord.createForEvent(ast.handler, ast.name, this.boundElementCount - 1);
|
BindingRecord.createForEvent(ast.handler, ast.fullName, this.boundElementCount - 1);
|
||||||
this.eventRecords.push(bindingRecord);
|
this.eventRecords.push(bindingRecord);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -138,17 +141,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
||||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||||
var directiveMetadata = ast.directive;
|
var directiveMetadata = ast.directive;
|
||||||
var changeDetectionMeta = directiveMetadata.changeDetection;
|
|
||||||
var directiveRecord = new DirectiveRecord({
|
var directiveRecord = new DirectiveRecord({
|
||||||
directiveIndex: directiveIndex,
|
directiveIndex: directiveIndex,
|
||||||
callAfterContentInit: changeDetectionMeta.callAfterContentInit,
|
callAfterContentInit:
|
||||||
callAfterContentChecked: changeDetectionMeta.callAfterContentChecked,
|
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1,
|
||||||
callAfterViewInit: changeDetectionMeta.callAfterViewInit,
|
callAfterContentChecked:
|
||||||
callAfterViewChecked: changeDetectionMeta.callAfterViewChecked,
|
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1,
|
||||||
callOnChanges: changeDetectionMeta.callOnChanges,
|
callAfterViewInit:
|
||||||
callDoCheck: changeDetectionMeta.callDoCheck,
|
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1,
|
||||||
callOnInit: changeDetectionMeta.callOnInit,
|
callAfterViewChecked:
|
||||||
changeDetection: changeDetectionMeta.changeDetection
|
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1,
|
||||||
|
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
||||||
|
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
||||||
|
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
|
||||||
|
changeDetection: directiveMetadata.changeDetection
|
||||||
});
|
});
|
||||||
this.directiveRecords.push(directiveRecord);
|
this.directiveRecords.push(directiveRecord);
|
||||||
|
|
||||||
@ -165,6 +171,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
templateVisitAll(this, ast.hostProperties, directiveRecord);
|
templateVisitAll(this, ast.hostProperties, directiveRecord);
|
||||||
templateVisitAll(this, ast.hostEvents, directiveRecord);
|
templateVisitAll(this, ast.hostEvents, directiveRecord);
|
||||||
|
templateVisitAll(this, ast.exportAsVars);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any {
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any {
|
||||||
@ -178,7 +185,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: TypeMetadata,
|
function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: CompileTypeMetadata,
|
||||||
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
|
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
|
||||||
var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors);
|
var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors);
|
||||||
return pvVisitors.map(pvVisitor => {
|
return pvVisitors.map(pvVisitor => {
|
||||||
@ -202,6 +209,7 @@ function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
|
function _protoViewId(hostComponentType: CompileTypeMetadata, pvIndex: number, viewType: string):
|
||||||
|
string {
|
||||||
return `${hostComponentType.name}_${viewType}_${pvIndex}`;
|
return `${hostComponentType.name}_${viewType}_${pvIndex}`;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {TypeMetadata} from './directive_metadata';
|
import {CompileTypeMetadata} from './directive_metadata';
|
||||||
import {SourceExpression, moduleRef} from './source_module';
|
import {SourceExpressions, moduleRef} from './source_module';
|
||||||
import {
|
import {
|
||||||
ChangeDetectorJITGenerator
|
ChangeDetectorJITGenerator
|
||||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||||
@ -32,7 +32,7 @@ var PREGEN_PROTO_CHANGE_DETECTOR_MODULE =
|
|||||||
export class ChangeDetectionCompiler {
|
export class ChangeDetectionCompiler {
|
||||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
||||||
|
|
||||||
compileComponentRuntime(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
compileComponentRuntime(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||||
parsedTemplate: TemplateAst[]): Function[] {
|
parsedTemplate: TemplateAst[]): Function[] {
|
||||||
var changeDetectorDefinitions =
|
var changeDetectorDefinitions =
|
||||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||||
@ -51,8 +51,8 @@ export class ChangeDetectionCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||||
parsedTemplate: TemplateAst[]): SourceExpression {
|
parsedTemplate: TemplateAst[]): SourceExpressions {
|
||||||
var changeDetectorDefinitions =
|
var changeDetectorDefinitions =
|
||||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||||
var factories = [];
|
var factories = [];
|
||||||
@ -75,7 +75,6 @@ export class ChangeDetectionCompiler {
|
|||||||
return codegen.generateSource();
|
return codegen.generateSource();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var expression = `[ ${factories.join(',')} ]`;
|
return new SourceExpressions(sourceParts, factories);
|
||||||
return new SourceExpression(sourceParts, expression);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {isPresent, Type} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank, Type, isString} from 'angular2/src/core/facade/lang';
|
||||||
|
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
TemplateCmd,
|
TemplateCmd,
|
||||||
text,
|
text,
|
||||||
@ -25,8 +26,8 @@ import {
|
|||||||
BoundDirectivePropertyAst,
|
BoundDirectivePropertyAst,
|
||||||
templateVisitAll
|
templateVisitAll
|
||||||
} from './template_ast';
|
} from './template_ast';
|
||||||
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||||
import {SourceExpression, moduleRef} from './source_module';
|
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||||
|
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {shimHostAttribute, shimContentAttribute} from './style_compiler';
|
import {shimHostAttribute, shimContentAttribute} from './style_compiler';
|
||||||
@ -34,21 +35,25 @@ import {escapeSingleQuoteString} from './util';
|
|||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
export var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
|
||||||
|
const IMPLICIT_VAR = '%implicit';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommandCompiler {
|
export class CommandCompiler {
|
||||||
compileComponentRuntime(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
changeDetectorFactories: Function[],
|
||||||
componentTemplateFactory: Function): TemplateCmd[] {
|
componentTemplateFactory: Function): TemplateCmd[] {
|
||||||
var visitor =
|
var visitor = new CommandBuilderVisitor(
|
||||||
new CommandBuilderVisitor(new RuntimeCommandFactory(componentTemplateFactory), component);
|
new RuntimeCommandFactory(componentTemplateFactory, changeDetectorFactories), component, 0);
|
||||||
templateVisitAll(visitor, template);
|
templateVisitAll(visitor, template);
|
||||||
return visitor.result;
|
return visitor.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(component: NormalizedDirectiveMetadata, template: TemplateAst[],
|
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
changeDetectorFactoryExpressions: string[],
|
||||||
componentTemplateFactory: Function): SourceExpression {
|
componentTemplateFactory: Function): SourceExpression {
|
||||||
var visitor =
|
var visitor = new CommandBuilderVisitor(
|
||||||
new CommandBuilderVisitor(new CodegenCommandFactory(componentTemplateFactory), component);
|
new CodegenCommandFactory(componentTemplateFactory, changeDetectorFactoryExpressions),
|
||||||
|
component, 0);
|
||||||
templateVisitAll(visitor, template);
|
templateVisitAll(visitor, template);
|
||||||
var source = `[${visitor.result.join(',')}]`;
|
var source = `[${visitor.result.join(',')}]`;
|
||||||
return new SourceExpression([], source);
|
return new SourceExpression([], source);
|
||||||
@ -58,22 +63,23 @@ export class CommandCompiler {
|
|||||||
interface CommandFactory<R> {
|
interface CommandFactory<R> {
|
||||||
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
||||||
createNgContent(ngContentIndex: number): R;
|
createNgContent(ngContentIndex: number): R;
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
isBound: boolean, ngContentIndex: number): R;
|
isBound: boolean, ngContentIndex: number): R;
|
||||||
createEndElement(): R;
|
createEndElement(): R;
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): R;
|
nativeShadow: boolean, ngContentIndex: number): R;
|
||||||
createEndComponent(): R;
|
createEndComponent(): R;
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
ngContentIndex: number, children: R[]): R;
|
isMerged: boolean, ngContentIndex: number, children: R[]): R;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||||
constructor(public componentTemplateFactory: Function) {}
|
constructor(public componentTemplateFactory: Function,
|
||||||
private _mapDirectives(directives: NormalizedDirectiveMetadata[]): Type[] {
|
public changeDetectorFactories: Function[]) {}
|
||||||
|
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
|
||||||
return directives.map(directive => directive.type.runtime);
|
return directives.map(directive => directive.type.runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,35 +87,46 @@ class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
|||||||
return text(value, isBound, ngContentIndex);
|
return text(value, isBound, ngContentIndex);
|
||||||
}
|
}
|
||||||
createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); }
|
createNgContent(ngContentIndex: number): TemplateCmd { return ngContent(ngContentIndex); }
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||||
return beginElement(name, attrNameAndValues, eventNames, variableNameAndValues,
|
return beginElement(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||||
this._mapDirectives(directives), isBound, ngContentIndex);
|
this._mapDirectives(directives), isBound, ngContentIndex);
|
||||||
}
|
}
|
||||||
createEndElement(): TemplateCmd { return endElement(); }
|
createEndElement(): TemplateCmd { return endElement(); }
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
nativeShadow: boolean, ngContentIndex: number): TemplateCmd {
|
||||||
return beginComponent(name, attrNameAndValues, eventNames, variableNameAndValues,
|
return beginComponent(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||||
this._mapDirectives(directives), nativeShadow, ngContentIndex,
|
this._mapDirectives(directives), nativeShadow, ngContentIndex,
|
||||||
this.componentTemplateFactory(directives[0]));
|
this.componentTemplateFactory(directives[0]));
|
||||||
}
|
}
|
||||||
createEndComponent(): TemplateCmd { return endComponent(); }
|
createEndComponent(): TemplateCmd { return endComponent(); }
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
ngContentIndex: number, children: TemplateCmd[]): TemplateCmd {
|
isMerged: boolean, ngContentIndex: number,
|
||||||
|
children: TemplateCmd[]): TemplateCmd {
|
||||||
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
return embeddedTemplate(attrNameAndValues, variableNameAndValues,
|
||||||
this._mapDirectives(directives), isMerged, ngContentIndex, children);
|
this._mapDirectives(directives), isMerged, ngContentIndex,
|
||||||
|
this.changeDetectorFactories[embeddedTemplateIndex], children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeStringArray(data: string[]): string {
|
function escapePrimitiveArray(data: any[]): string {
|
||||||
return `[${data.map( value => escapeSingleQuoteString(value)).join(',')}]`;
|
return `[${data.map( (value) => {
|
||||||
|
if (isString(value)) {
|
||||||
|
return escapeSingleQuoteString(value);
|
||||||
|
} else if (isBlank(value)) {
|
||||||
|
return 'null';
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}).join(',')}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CodegenCommandFactory implements CommandFactory<string> {
|
class CodegenCommandFactory implements CommandFactory<string> {
|
||||||
constructor(public componentTemplateFactory: Function) {}
|
constructor(public componentTemplateFactory: Function,
|
||||||
|
public changeDetectorFactoryExpressions: string[]) {}
|
||||||
|
|
||||||
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
createText(value: string, isBound: boolean, ngContentIndex: number): string {
|
||||||
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}text(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`;
|
||||||
@ -117,26 +134,27 @@ class CodegenCommandFactory implements CommandFactory<string> {
|
|||||||
createNgContent(ngContentIndex: number): string {
|
createNgContent(ngContentIndex: number): string {
|
||||||
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}ngContent(${ngContentIndex})`;
|
||||||
}
|
}
|
||||||
createBeginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
isBound: boolean, ngContentIndex: number): string {
|
isBound: boolean, ngContentIndex: number): string {
|
||||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}beginElement(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isBound}, ${ngContentIndex})`;
|
||||||
}
|
}
|
||||||
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
|
createEndElement(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endElement()`; }
|
||||||
createBeginComponent(name: string, attrNameAndValues: string[], eventNames: string[],
|
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||||
variableNameAndValues: string[], directives: NormalizedDirectiveMetadata[],
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
nativeShadow: boolean, ngContentIndex: number): string {
|
nativeShadow: boolean, ngContentIndex: number): string {
|
||||||
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(eventNames)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}beginComponent(${escapeSingleQuoteString(name)}, ${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(eventTargetAndNames)}, ${escapePrimitiveArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${nativeShadow}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`;
|
||||||
}
|
}
|
||||||
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
|
createEndComponent(): string { return `${TEMPLATE_COMMANDS_MODULE_REF}endComponent()`; }
|
||||||
createEmbeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||||
directives: NormalizedDirectiveMetadata[], isMerged: boolean,
|
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||||
ngContentIndex: number, children: string[]): string {
|
isMerged: boolean, ngContentIndex: number, children: string[]): string {
|
||||||
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapeStringArray(attrNameAndValues)}, ${escapeStringArray(variableNameAndValues)}, [${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, [${children.join(',')}])`;
|
return `${TEMPLATE_COMMANDS_MODULE_REF}embeddedTemplate(${escapePrimitiveArray(attrNameAndValues)}, ${escapePrimitiveArray(variableNameAndValues)}, ` +
|
||||||
|
`[${_escapeDirectives(directives).join(',')}], ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, [${children.join(',')}])`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _escapeDirectives(directives: NormalizedDirectiveMetadata[]): string[] {
|
function _escapeDirectives(directives: CompileDirectiveMetadata[]): string[] {
|
||||||
return directives.map(directiveType =>
|
return directives.map(directiveType =>
|
||||||
`${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`);
|
`${moduleRef(directiveType.type.moduleId)}${directiveType.type.name}`);
|
||||||
}
|
}
|
||||||
@ -150,10 +168,11 @@ function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
|||||||
class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||||
result: R[] = [];
|
result: R[] = [];
|
||||||
transitiveNgContentCount: number = 0;
|
transitiveNgContentCount: number = 0;
|
||||||
constructor(public commandFactory: CommandFactory<R>,
|
constructor(public commandFactory: CommandFactory<R>, public component: CompileDirectiveMetadata,
|
||||||
public component: NormalizedDirectiveMetadata) {}
|
public embeddedTemplateIndex: number) {}
|
||||||
|
|
||||||
private _readAttrNameAndValues(localComponent: NormalizedDirectiveMetadata,
|
private _readAttrNameAndValues(localComponent: CompileDirectiveMetadata,
|
||||||
|
directives: CompileDirectiveMetadata[],
|
||||||
attrAsts: TemplateAst[]): string[] {
|
attrAsts: TemplateAst[]): string[] {
|
||||||
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
||||||
if (isPresent(localComponent) &&
|
if (isPresent(localComponent) &&
|
||||||
@ -165,7 +184,13 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
|||||||
attrNameAndValues.push(shimContentAttribute(this.component.type.id));
|
attrNameAndValues.push(shimContentAttribute(this.component.type.id));
|
||||||
attrNameAndValues.push('');
|
attrNameAndValues.push('');
|
||||||
}
|
}
|
||||||
return attrNameAndValues;
|
directives.forEach(directiveMeta => {
|
||||||
|
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||||
|
attrNameAndValues.push(name);
|
||||||
|
attrNameAndValues.push(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return removeKeyValueArrayDuplicates(attrNameAndValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any {
|
visitNgContent(ast: NgContentAst, context: any): any {
|
||||||
@ -174,43 +199,61 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||||
var childVisitor = new CommandBuilderVisitor(this.commandFactory, this.component);
|
this.embeddedTemplateIndex++;
|
||||||
|
var childVisitor =
|
||||||
|
new CommandBuilderVisitor(this.commandFactory, this.component, this.embeddedTemplateIndex);
|
||||||
templateVisitAll(childVisitor, ast.children);
|
templateVisitAll(childVisitor, ast.children);
|
||||||
var isMerged = childVisitor.transitiveNgContentCount > 0;
|
var isMerged = childVisitor.transitiveNgContentCount > 0;
|
||||||
this.transitiveNgContentCount += childVisitor.transitiveNgContentCount;
|
var variableNameAndValues = [];
|
||||||
var directivesAndEventNames = visitAndReturnContext(this, ast.directives, [[], []]);
|
ast.vars.forEach((varAst) => {
|
||||||
|
variableNameAndValues.push(varAst.name);
|
||||||
|
variableNameAndValues.push(varAst.value);
|
||||||
|
});
|
||||||
|
var directives = [];
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||||
|
directiveAst.visit(this, new DirectiveContext(index, [], [], directives));
|
||||||
|
});
|
||||||
this.result.push(this.commandFactory.createEmbeddedTemplate(
|
this.result.push(this.commandFactory.createEmbeddedTemplate(
|
||||||
this._readAttrNameAndValues(null, ast.attrs), visitAndReturnContext(this, ast.vars, []),
|
this.embeddedTemplateIndex, this._readAttrNameAndValues(null, directives, ast.attrs),
|
||||||
directivesAndEventNames[0], isMerged, ast.ngContentIndex, childVisitor.result));
|
variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result));
|
||||||
|
this.transitiveNgContentCount += childVisitor.transitiveNgContentCount;
|
||||||
|
this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
visitElement(ast: ElementAst, context: any): any {
|
||||||
var component = ast.getComponent();
|
var component = ast.getComponent();
|
||||||
var eventNames = visitAndReturnContext(this, ast.events, []);
|
var eventTargetAndNames = visitAndReturnContext(this, ast.events, []);
|
||||||
|
var variableNameAndValues = [];
|
||||||
|
if (isBlank(component)) {
|
||||||
|
ast.exportAsVars.forEach((varAst) => {
|
||||||
|
variableNameAndValues.push(varAst.name);
|
||||||
|
variableNameAndValues.push(IMPLICIT_VAR);
|
||||||
|
});
|
||||||
|
}
|
||||||
var directives = [];
|
var directives = [];
|
||||||
visitAndReturnContext(this, ast.directives, [directives, eventNames]);
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||||
var attrNameAndValues = this._readAttrNameAndValues(component, ast.attrs);
|
directiveAst.visit(this, new DirectiveContext(index, eventTargetAndNames,
|
||||||
var vars = visitAndReturnContext(this, ast.vars, []);
|
variableNameAndValues, directives));
|
||||||
|
});
|
||||||
|
eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames);
|
||||||
|
|
||||||
|
var attrNameAndValues = this._readAttrNameAndValues(component, directives, ast.attrs);
|
||||||
if (isPresent(component)) {
|
if (isPresent(component)) {
|
||||||
this.result.push(this.commandFactory.createBeginComponent(
|
this.result.push(this.commandFactory.createBeginComponent(
|
||||||
ast.name, attrNameAndValues, eventNames, vars, directives,
|
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
|
||||||
component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex));
|
component.template.encapsulation === ViewEncapsulation.Native, ast.ngContentIndex));
|
||||||
templateVisitAll(this, ast.children);
|
templateVisitAll(this, ast.children);
|
||||||
this.result.push(this.commandFactory.createEndComponent());
|
this.result.push(this.commandFactory.createEndComponent());
|
||||||
} else {
|
} else {
|
||||||
this.result.push(this.commandFactory.createBeginElement(ast.name, attrNameAndValues,
|
this.result.push(this.commandFactory.createBeginElement(
|
||||||
eventNames, vars, directives,
|
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
|
||||||
ast.isBound(), ast.ngContentIndex));
|
ast.isBound(), ast.ngContentIndex));
|
||||||
templateVisitAll(this, ast.children);
|
templateVisitAll(this, ast.children);
|
||||||
this.result.push(this.commandFactory.createEndElement());
|
this.result.push(this.commandFactory.createEndElement());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitVariable(ast: VariableAst, variableNameAndValues: string[]): any {
|
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||||
variableNameAndValues.push(ast.name);
|
|
||||||
variableNameAndValues.push(ast.value);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitAttr(ast: AttrAst, attrNameAndValues: string[]): any {
|
visitAttr(ast: AttrAst, attrNameAndValues: string[]): any {
|
||||||
attrNameAndValues.push(ast.name);
|
attrNameAndValues.push(ast.name);
|
||||||
attrNameAndValues.push(ast.value);
|
attrNameAndValues.push(ast.value);
|
||||||
@ -224,15 +267,42 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
|||||||
this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex));
|
this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDirective(ast: DirectiveAst, directivesAndEventNames: any[][]): any {
|
visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
|
||||||
directivesAndEventNames[0].push(ast.directive);
|
ctx.targetDirectives.push(ast.directive);
|
||||||
templateVisitAll(this, ast.hostEvents, directivesAndEventNames[1]);
|
templateVisitAll(this, ast.hostEvents, ctx.eventTargetAndNames);
|
||||||
|
ast.exportAsVars.forEach(varAst => {
|
||||||
|
ctx.targetVariableNameAndValues.push(varAst.name);
|
||||||
|
ctx.targetVariableNameAndValues.push(ctx.index);
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitEvent(ast: BoundEventAst, eventNames: string[]): any {
|
visitEvent(ast: BoundEventAst, eventTargetAndNames: string[]): any {
|
||||||
eventNames.push(ast.getFullName());
|
eventTargetAndNames.push(ast.target);
|
||||||
|
eventTargetAndNames.push(ast.name);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeKeyValueArrayDuplicates(keyValueArray: string[]): string[] {
|
||||||
|
var knownPairs = new Set();
|
||||||
|
var resultKeyValueArray = [];
|
||||||
|
for (var i = 0; i < keyValueArray.length; i += 2) {
|
||||||
|
var key = keyValueArray[i];
|
||||||
|
var value = keyValueArray[i + 1];
|
||||||
|
var pairId = `${key}:${value}`;
|
||||||
|
if (!SetWrapper.has(knownPairs, pairId)) {
|
||||||
|
resultKeyValueArray.push(key);
|
||||||
|
resultKeyValueArray.push(value);
|
||||||
|
knownPairs.add(pairId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultKeyValueArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectiveContext {
|
||||||
|
constructor(public index: number, public eventTargetAndNames: string[],
|
||||||
|
public targetVariableNameAndValues: any[],
|
||||||
|
public targetDirectives: CompileDirectiveMetadata[]) {}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
export {TemplateCompiler} from './template_compiler';
|
export {TemplateCompiler} from './template_compiler';
|
||||||
export {
|
export {
|
||||||
DirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
TemplateMetadata,
|
CompileTemplateMetadata
|
||||||
ChangeDetectionMetadata,
|
|
||||||
INormalizedDirectiveMetadata
|
|
||||||
} from './directive_metadata';
|
} from './directive_metadata';
|
||||||
export {SourceModule, SourceWithImports} from './source_module';
|
export {SourceModule, SourceWithImports} from './source_module';
|
||||||
|
@ -1,12 +1,27 @@
|
|||||||
import {isPresent, normalizeBool, serializeEnum, Type} from 'angular2/src/core/facade/lang';
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
normalizeBool,
|
||||||
|
serializeEnum,
|
||||||
|
Type,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper
|
||||||
|
} from 'angular2/src/core/facade/lang';
|
||||||
|
import {StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
changeDetectionStrategyFromJson
|
CHANGE_DECTION_STRATEGY_VALUES
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {ViewEncapsulation, viewEncapsulationFromJson} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/render/api';
|
||||||
import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector';
|
import {CssSelector} from 'angular2/src/core/render/dom/compiler/selector';
|
||||||
|
import {splitAtColon} from './util';
|
||||||
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces';
|
||||||
|
|
||||||
export class TypeMetadata {
|
// group 1: "property" from "[property]"
|
||||||
|
// group 2: "event" from "(event)"
|
||||||
|
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||||
|
|
||||||
|
export class CompileTypeMetadata {
|
||||||
id: number;
|
id: number;
|
||||||
runtime: Type;
|
runtime: Type;
|
||||||
name: string;
|
name: string;
|
||||||
@ -19,8 +34,9 @@ export class TypeMetadata {
|
|||||||
this.moduleId = moduleId;
|
this.moduleId = moduleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): TypeMetadata {
|
static fromJson(data: StringMap<string, any>): CompileTypeMetadata {
|
||||||
return new TypeMetadata({id: data['id'], name: data['name'], moduleId: data['moduleId']});
|
return new CompileTypeMetadata(
|
||||||
|
{id: data['id'], name: data['name'], moduleId: data['moduleId']});
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): StringMap<string, any> {
|
toJson(): StringMap<string, any> {
|
||||||
@ -33,169 +49,39 @@ export class TypeMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetectionMetadata {
|
export class CompileTemplateMetadata {
|
||||||
changeDetection: ChangeDetectionStrategy;
|
|
||||||
properties: string[];
|
|
||||||
events: string[];
|
|
||||||
hostListeners: StringMap<string, string>;
|
|
||||||
hostProperties: StringMap<string, string>;
|
|
||||||
callAfterContentInit: boolean;
|
|
||||||
callAfterContentChecked: boolean;
|
|
||||||
callAfterViewInit: boolean;
|
|
||||||
callAfterViewChecked: boolean;
|
|
||||||
callOnChanges: boolean;
|
|
||||||
callDoCheck: boolean;
|
|
||||||
callOnInit: boolean;
|
|
||||||
constructor({changeDetection, properties, events, hostListeners, hostProperties,
|
|
||||||
callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
|
||||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit}: {
|
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
|
||||||
properties?: string[],
|
|
||||||
events?: string[],
|
|
||||||
hostListeners?: StringMap<string, string>,
|
|
||||||
hostProperties?: StringMap<string, string>,
|
|
||||||
callAfterContentInit?: boolean,
|
|
||||||
callAfterContentChecked?: boolean,
|
|
||||||
callAfterViewInit?: boolean,
|
|
||||||
callAfterViewChecked?: boolean,
|
|
||||||
callOnChanges?: boolean,
|
|
||||||
callDoCheck?: boolean,
|
|
||||||
callOnInit?: boolean
|
|
||||||
} = {}) {
|
|
||||||
this.changeDetection = changeDetection;
|
|
||||||
this.properties = isPresent(properties) ? properties : [];
|
|
||||||
this.events = isPresent(events) ? events : [];
|
|
||||||
this.hostListeners = isPresent(hostListeners) ? hostListeners : {};
|
|
||||||
this.hostProperties = isPresent(hostProperties) ? hostProperties : {};
|
|
||||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
|
||||||
this.callAfterContentChecked = normalizeBool(callAfterContentChecked);
|
|
||||||
this.callAfterViewInit = normalizeBool(callAfterViewInit);
|
|
||||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
|
||||||
this.callOnChanges = normalizeBool(callOnChanges);
|
|
||||||
this.callDoCheck = normalizeBool(callDoCheck);
|
|
||||||
this.callOnInit = normalizeBool(callOnInit);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): ChangeDetectionMetadata {
|
|
||||||
return new ChangeDetectionMetadata({
|
|
||||||
changeDetection: isPresent(data['changeDetection']) ?
|
|
||||||
changeDetectionStrategyFromJson(data['changeDetection']) :
|
|
||||||
data['changeDetection'],
|
|
||||||
properties: data['properties'],
|
|
||||||
events: data['events'],
|
|
||||||
hostListeners: data['hostListeners'],
|
|
||||||
hostProperties: data['hostProperties'],
|
|
||||||
callAfterContentInit: data['callAfterContentInit'],
|
|
||||||
callAfterContentChecked: data['callAfterContentChecked'],
|
|
||||||
callAfterViewInit: data['callAfterViewInit'],
|
|
||||||
callAfterViewChecked: data['callAfterViewChecked'],
|
|
||||||
callOnChanges: data['callOnChanges'],
|
|
||||||
callDoCheck: data['callDoCheck'],
|
|
||||||
callOnInit: data['callOnInit']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson(): StringMap<string, any> {
|
|
||||||
return {
|
|
||||||
'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) :
|
|
||||||
this.changeDetection,
|
|
||||||
'properties': this.properties,
|
|
||||||
'events': this.events,
|
|
||||||
'hostListeners': this.hostListeners,
|
|
||||||
'hostProperties': this.hostProperties,
|
|
||||||
'callAfterContentInit': this.callAfterContentInit,
|
|
||||||
'callAfterContentChecked': this.callAfterContentChecked,
|
|
||||||
'callAfterViewInit': this.callAfterViewInit,
|
|
||||||
'callAfterViewChecked': this.callAfterViewChecked,
|
|
||||||
'callOnChanges': this.callOnChanges,
|
|
||||||
'callDoCheck': this.callDoCheck,
|
|
||||||
'callOnInit': this.callOnInit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TemplateMetadata {
|
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
template: string;
|
template: string;
|
||||||
templateUrl: string;
|
templateUrl: string;
|
||||||
styles: string[];
|
styles: string[];
|
||||||
styleUrls: string[];
|
styleUrls: string[];
|
||||||
hostAttributes: StringMap<string, string>;
|
ngContentSelectors: string[];
|
||||||
constructor({encapsulation, template, templateUrl, styles, styleUrls, hostAttributes}: {
|
constructor({encapsulation, template, templateUrl, styles, styleUrls, ngContentSelectors}: {
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
template?: string,
|
template?: string,
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
hostAttributes?: StringMap<string, string>
|
ngContentSelectors?: string[]
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.encapsulation = isPresent(encapsulation) ? encapsulation : ViewEncapsulation.None;
|
this.encapsulation = encapsulation;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.templateUrl = templateUrl;
|
this.templateUrl = templateUrl;
|
||||||
this.styles = isPresent(styles) ? styles : [];
|
this.styles = isPresent(styles) ? styles : [];
|
||||||
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
this.styleUrls = isPresent(styleUrls) ? styleUrls : [];
|
||||||
this.hostAttributes = isPresent(hostAttributes) ? hostAttributes : {};
|
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class DirectiveMetadata {
|
|
||||||
type: TypeMetadata;
|
|
||||||
isComponent: boolean;
|
|
||||||
dynamicLoadable: boolean;
|
|
||||||
selector: string;
|
|
||||||
changeDetection: ChangeDetectionMetadata;
|
|
||||||
template: TemplateMetadata;
|
|
||||||
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
|
||||||
type?: TypeMetadata,
|
|
||||||
isComponent?: boolean,
|
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
selector?: string,
|
|
||||||
changeDetection?: ChangeDetectionMetadata,
|
|
||||||
template?: TemplateMetadata
|
|
||||||
} = {}) {
|
|
||||||
this.type = type;
|
|
||||||
this.isComponent = normalizeBool(isComponent);
|
|
||||||
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
|
||||||
this.selector = selector;
|
|
||||||
this.changeDetection = changeDetection;
|
|
||||||
this.template = template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NormalizedTemplateMetadata {
|
|
||||||
encapsulation: ViewEncapsulation;
|
|
||||||
template: string;
|
|
||||||
styles: string[];
|
|
||||||
styleAbsUrls: string[];
|
|
||||||
ngContentSelectors: string[];
|
|
||||||
hostAttributes: StringMap<string, string>;
|
|
||||||
constructor({encapsulation, template, styles, styleAbsUrls, ngContentSelectors, hostAttributes}: {
|
|
||||||
encapsulation?: ViewEncapsulation,
|
|
||||||
template?: string,
|
|
||||||
styles?: string[],
|
|
||||||
styleAbsUrls?: string[],
|
|
||||||
ngContentSelectors?: string[],
|
|
||||||
hostAttributes?: StringMap<string, string>
|
|
||||||
} = {}) {
|
|
||||||
this.encapsulation = encapsulation;
|
|
||||||
this.template = template;
|
|
||||||
this.styles = styles;
|
|
||||||
this.styleAbsUrls = styleAbsUrls;
|
|
||||||
this.ngContentSelectors = ngContentSelectors;
|
|
||||||
this.hostAttributes = hostAttributes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): NormalizedTemplateMetadata {
|
static fromJson(data: StringMap<string, any>): CompileTemplateMetadata {
|
||||||
return new NormalizedTemplateMetadata({
|
return new CompileTemplateMetadata({
|
||||||
encapsulation: isPresent(data['encapsulation']) ?
|
encapsulation: isPresent(data['encapsulation']) ?
|
||||||
viewEncapsulationFromJson(data['encapsulation']) :
|
VIEW_ENCAPSULATION_VALUES[data['encapsulation']] :
|
||||||
data['encapsulation'],
|
data['encapsulation'],
|
||||||
template: data['template'],
|
template: data['template'],
|
||||||
|
templateUrl: data['templateUrl'],
|
||||||
styles: data['styles'],
|
styles: data['styles'],
|
||||||
styleAbsUrls: data['styleAbsUrls'],
|
styleUrls: data['styleUrls'],
|
||||||
ngContentSelectors: data['ngContentSelectors'],
|
ngContentSelectors: data['ngContentSelectors']
|
||||||
hostAttributes: data['hostAttributes']
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,51 +90,141 @@ export class NormalizedTemplateMetadata {
|
|||||||
'encapsulation':
|
'encapsulation':
|
||||||
isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : this.encapsulation,
|
isPresent(this.encapsulation) ? serializeEnum(this.encapsulation) : this.encapsulation,
|
||||||
'template': this.template,
|
'template': this.template,
|
||||||
|
'templateUrl': this.templateUrl,
|
||||||
'styles': this.styles,
|
'styles': this.styles,
|
||||||
'styleAbsUrls': this.styleAbsUrls,
|
'styleUrls': this.styleUrls,
|
||||||
'ngContentSelectors': this.ngContentSelectors,
|
'ngContentSelectors': this.ngContentSelectors
|
||||||
'hostAttributes': this.hostAttributes
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INormalizedDirectiveMetadata {}
|
export class CompileDirectiveMetadata {
|
||||||
|
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection,
|
||||||
export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata {
|
properties, events, host, lifecycleHooks, template}: {
|
||||||
type: TypeMetadata;
|
type?: CompileTypeMetadata,
|
||||||
isComponent: boolean;
|
|
||||||
dynamicLoadable: boolean;
|
|
||||||
selector: string;
|
|
||||||
changeDetection: ChangeDetectionMetadata;
|
|
||||||
template: NormalizedTemplateMetadata;
|
|
||||||
constructor({type, isComponent, dynamicLoadable, selector, changeDetection, template}: {
|
|
||||||
id?: number,
|
|
||||||
type?: TypeMetadata,
|
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
dynamicLoadable?: boolean,
|
dynamicLoadable?: boolean,
|
||||||
selector?: string,
|
selector?: string,
|
||||||
changeDetection?: ChangeDetectionMetadata,
|
exportAs?: string,
|
||||||
template?: NormalizedTemplateMetadata
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
properties?: string[],
|
||||||
|
events?: string[],
|
||||||
|
host?: StringMap<string, string>,
|
||||||
|
lifecycleHooks?: LifecycleHooks[],
|
||||||
|
template?: CompileTemplateMetadata
|
||||||
|
} = {}): CompileDirectiveMetadata {
|
||||||
|
var hostListeners = {};
|
||||||
|
var hostProperties = {};
|
||||||
|
var hostAttributes = {};
|
||||||
|
if (isPresent(host)) {
|
||||||
|
StringMapWrapper.forEach(host, (value: string, key: string) => {
|
||||||
|
var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key);
|
||||||
|
if (isBlank(matches)) {
|
||||||
|
hostAttributes[key] = value;
|
||||||
|
} else if (isPresent(matches[1])) {
|
||||||
|
hostProperties[matches[1]] = value;
|
||||||
|
} else if (isPresent(matches[2])) {
|
||||||
|
hostListeners[matches[2]] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var propsMap = {};
|
||||||
|
if (isPresent(properties)) {
|
||||||
|
properties.forEach((bindConfig: string) => {
|
||||||
|
// canonical syntax: `dirProp: elProp`
|
||||||
|
// if there is no `:`, use dirProp = elProp
|
||||||
|
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||||
|
propsMap[parts[0]] = parts[1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var eventsMap = {};
|
||||||
|
if (isPresent(events)) {
|
||||||
|
events.forEach((bindConfig: string) => {
|
||||||
|
// canonical syntax: `dirProp: elProp`
|
||||||
|
// if there is no `:`, use dirProp = elProp
|
||||||
|
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||||
|
eventsMap[parts[0]] = parts[1];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompileDirectiveMetadata({
|
||||||
|
type: type,
|
||||||
|
isComponent: normalizeBool(isComponent),
|
||||||
|
dynamicLoadable: normalizeBool(dynamicLoadable),
|
||||||
|
selector: selector,
|
||||||
|
exportAs: exportAs,
|
||||||
|
changeDetection: changeDetection,
|
||||||
|
properties: propsMap,
|
||||||
|
events: eventsMap,
|
||||||
|
hostListeners: hostListeners,
|
||||||
|
hostProperties: hostProperties,
|
||||||
|
hostAttributes: hostAttributes,
|
||||||
|
lifecycleHooks: isPresent(lifecycleHooks) ? lifecycleHooks : [], template: template
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type: CompileTypeMetadata;
|
||||||
|
isComponent: boolean;
|
||||||
|
dynamicLoadable: boolean;
|
||||||
|
selector: string;
|
||||||
|
exportAs: string;
|
||||||
|
changeDetection: ChangeDetectionStrategy;
|
||||||
|
properties: StringMap<string, string>;
|
||||||
|
events: StringMap<string, string>;
|
||||||
|
hostListeners: StringMap<string, string>;
|
||||||
|
hostProperties: StringMap<string, string>;
|
||||||
|
hostAttributes: StringMap<string, string>;
|
||||||
|
lifecycleHooks: LifecycleHooks[];
|
||||||
|
template: CompileTemplateMetadata;
|
||||||
|
constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, properties,
|
||||||
|
events, hostListeners, hostProperties, hostAttributes, lifecycleHooks, template}: {
|
||||||
|
type?: CompileTypeMetadata,
|
||||||
|
isComponent?: boolean,
|
||||||
|
dynamicLoadable?: boolean,
|
||||||
|
selector?: string,
|
||||||
|
exportAs?: string,
|
||||||
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
properties?: StringMap<string, string>,
|
||||||
|
events?: StringMap<string, string>,
|
||||||
|
hostListeners?: StringMap<string, string>,
|
||||||
|
hostProperties?: StringMap<string, string>,
|
||||||
|
hostAttributes?: StringMap<string, string>,
|
||||||
|
lifecycleHooks?: LifecycleHooks[],
|
||||||
|
template?: CompileTemplateMetadata
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isComponent = normalizeBool(isComponent);
|
this.isComponent = isComponent;
|
||||||
this.dynamicLoadable = normalizeBool(dynamicLoadable);
|
this.dynamicLoadable = dynamicLoadable;
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
|
this.exportAs = exportAs;
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
|
this.properties = properties;
|
||||||
|
this.events = events;
|
||||||
|
this.hostListeners = hostListeners;
|
||||||
|
this.hostProperties = hostProperties;
|
||||||
|
this.hostAttributes = hostAttributes;
|
||||||
|
this.lifecycleHooks = lifecycleHooks;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: StringMap<string, any>): NormalizedDirectiveMetadata {
|
static fromJson(data: StringMap<string, any>): CompileDirectiveMetadata {
|
||||||
return new NormalizedDirectiveMetadata({
|
return new CompileDirectiveMetadata({
|
||||||
isComponent: data['isComponent'],
|
isComponent: data['isComponent'],
|
||||||
dynamicLoadable: data['dynamicLoadable'],
|
dynamicLoadable: data['dynamicLoadable'],
|
||||||
selector: data['selector'],
|
selector: data['selector'],
|
||||||
type: isPresent(data['type']) ? TypeMetadata.fromJson(data['type']) : data['type'],
|
exportAs: data['exportAs'],
|
||||||
|
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
|
||||||
changeDetection: isPresent(data['changeDetection']) ?
|
changeDetection: isPresent(data['changeDetection']) ?
|
||||||
ChangeDetectionMetadata.fromJson(data['changeDetection']) :
|
CHANGE_DECTION_STRATEGY_VALUES[data['changeDetection']] :
|
||||||
data['changeDetection'],
|
data['changeDetection'],
|
||||||
template:
|
properties: data['properties'],
|
||||||
isPresent(data['template']) ? NormalizedTemplateMetadata.fromJson(data['template']) :
|
events: data['events'],
|
||||||
|
hostListeners: data['hostListeners'],
|
||||||
|
hostProperties: data['hostProperties'],
|
||||||
|
hostAttributes: data['hostAttributes'],
|
||||||
|
lifecycleHooks:
|
||||||
|
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
|
||||||
|
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
|
||||||
data['template']
|
data['template']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -258,45 +234,38 @@ export class NormalizedDirectiveMetadata implements INormalizedDirectiveMetadata
|
|||||||
'isComponent': this.isComponent,
|
'isComponent': this.isComponent,
|
||||||
'dynamicLoadable': this.dynamicLoadable,
|
'dynamicLoadable': this.dynamicLoadable,
|
||||||
'selector': this.selector,
|
'selector': this.selector,
|
||||||
|
'exportAs': this.exportAs,
|
||||||
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
||||||
'changeDetection':
|
'changeDetection': isPresent(this.changeDetection) ? serializeEnum(this.changeDetection) :
|
||||||
isPresent(this.changeDetection) ? this.changeDetection.toJson() : this.changeDetection,
|
this.changeDetection,
|
||||||
|
'properties': this.properties,
|
||||||
|
'events': this.events,
|
||||||
|
'hostListeners': this.hostListeners,
|
||||||
|
'hostProperties': this.hostProperties,
|
||||||
|
'hostAttributes': this.hostAttributes,
|
||||||
|
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
|
||||||
'template': isPresent(this.template) ? this.template.toJson() : this.template
|
'template': isPresent(this.template) ? this.template.toJson() : this.template
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHostComponentMeta(componentType: TypeMetadata, componentSelector: string):
|
export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||||
NormalizedDirectiveMetadata {
|
componentSelector: string): CompileDirectiveMetadata {
|
||||||
var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate();
|
var template = CssSelector.parse(componentSelector)[0].getMatchingElementTemplate();
|
||||||
return new NormalizedDirectiveMetadata({
|
return CompileDirectiveMetadata.create({
|
||||||
type: new TypeMetadata({
|
type: new CompileTypeMetadata({
|
||||||
runtime: Object,
|
runtime: Object,
|
||||||
id: (componentType.id * -1) - 1,
|
id: (componentType.id * -1) - 1,
|
||||||
name: `Host${componentType.name}`,
|
name: `Host${componentType.name}`,
|
||||||
moduleId: componentType.moduleId
|
moduleId: componentType.moduleId
|
||||||
}),
|
}),
|
||||||
template: new NormalizedTemplateMetadata({
|
template: new CompileTemplateMetadata(
|
||||||
template: template,
|
{template: template, templateUrl: '', styles: [], styleUrls: [], ngContentSelectors: []}),
|
||||||
styles: [],
|
|
||||||
styleAbsUrls: [],
|
|
||||||
hostAttributes: {},
|
|
||||||
ngContentSelectors: []
|
|
||||||
}),
|
|
||||||
changeDetection: new ChangeDetectionMetadata({
|
|
||||||
changeDetection: ChangeDetectionStrategy.Default,
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
properties: [],
|
properties: [],
|
||||||
events: [],
|
events: [],
|
||||||
hostListeners: {},
|
host: {},
|
||||||
hostProperties: {},
|
lifecycleHooks: [],
|
||||||
callAfterContentInit: false,
|
|
||||||
callAfterContentChecked: false,
|
|
||||||
callAfterViewInit: false,
|
|
||||||
callAfterViewChecked: false,
|
|
||||||
callOnChanges: false,
|
|
||||||
callDoCheck: false,
|
|
||||||
callOnInit: false
|
|
||||||
}),
|
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
dynamicLoadable: false,
|
dynamicLoadable: false,
|
||||||
selector: '*'
|
selector: '*'
|
||||||
|
@ -8,14 +8,14 @@ import {
|
|||||||
RegExpWrapper
|
RegExpWrapper
|
||||||
} from 'angular2/src/core/facade/lang';
|
} from 'angular2/src/core/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
import {MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import * as cpl from './directive_metadata';
|
import * as cpl from './directive_metadata';
|
||||||
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||||
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
@ -26,81 +26,55 @@ var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class RuntimeMetadataResolver {
|
export class RuntimeMetadataResolver {
|
||||||
private _directiveCounter = 0;
|
private _directiveCounter = 0;
|
||||||
private _cache: Map<Type, cpl.DirectiveMetadata> = new Map();
|
private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map();
|
||||||
|
|
||||||
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
||||||
|
|
||||||
getMetadata(directiveType: Type): cpl.DirectiveMetadata {
|
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||||
var meta = this._cache.get(directiveType);
|
var meta = this._cache.get(directiveType);
|
||||||
if (isBlank(meta)) {
|
if (isBlank(meta)) {
|
||||||
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
||||||
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
||||||
var templateMeta = null;
|
var templateMeta = null;
|
||||||
var hostListeners = {};
|
|
||||||
var hostProperties = {};
|
|
||||||
var hostAttributes = {};
|
|
||||||
var changeDetectionStrategy = null;
|
var changeDetectionStrategy = null;
|
||||||
var dynamicLoadable: boolean = false;
|
|
||||||
|
|
||||||
if (isPresent(directiveAnnotation.host)) {
|
|
||||||
StringMapWrapper.forEach(directiveAnnotation.host, (value: string, key: string) => {
|
|
||||||
var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key);
|
|
||||||
if (isBlank(matches)) {
|
|
||||||
hostAttributes[key] = value;
|
|
||||||
} else if (isPresent(matches[1])) {
|
|
||||||
hostProperties[matches[1]] = value;
|
|
||||||
} else if (isPresent(matches[2])) {
|
|
||||||
hostListeners[matches[2]] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
||||||
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
||||||
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
||||||
templateMeta = new cpl.TemplateMetadata({
|
templateMeta = new cpl.CompileTemplateMetadata({
|
||||||
encapsulation: viewAnnotation.encapsulation,
|
encapsulation: viewAnnotation.encapsulation,
|
||||||
template: viewAnnotation.template,
|
template: viewAnnotation.template,
|
||||||
templateUrl: viewAnnotation.templateUrl,
|
templateUrl: viewAnnotation.templateUrl,
|
||||||
styles: viewAnnotation.styles,
|
styles: viewAnnotation.styles,
|
||||||
styleUrls: viewAnnotation.styleUrls,
|
styleUrls: viewAnnotation.styleUrls
|
||||||
hostAttributes: hostAttributes
|
|
||||||
});
|
});
|
||||||
changeDetectionStrategy = compAnnotation.changeDetection;
|
changeDetectionStrategy = compAnnotation.changeDetection;
|
||||||
dynamicLoadable = compAnnotation.dynamicLoadable;
|
|
||||||
}
|
}
|
||||||
meta = new cpl.DirectiveMetadata({
|
meta = cpl.CompileDirectiveMetadata.create({
|
||||||
selector: directiveAnnotation.selector,
|
selector: directiveAnnotation.selector,
|
||||||
|
exportAs: directiveAnnotation.exportAs,
|
||||||
isComponent: isPresent(templateMeta),
|
isComponent: isPresent(templateMeta),
|
||||||
dynamicLoadable: dynamicLoadable,
|
dynamicLoadable: true,
|
||||||
type: new cpl.TypeMetadata({
|
type: new cpl.CompileTypeMetadata({
|
||||||
id: this._directiveCounter++,
|
id: this._directiveCounter++,
|
||||||
name: stringify(directiveType),
|
name: stringify(directiveType),
|
||||||
moduleId: moduleId,
|
moduleId: moduleId,
|
||||||
runtime: directiveType
|
runtime: directiveType
|
||||||
}),
|
}),
|
||||||
template: templateMeta,
|
template: templateMeta,
|
||||||
changeDetection: new cpl.ChangeDetectionMetadata({
|
|
||||||
changeDetection: changeDetectionStrategy,
|
changeDetection: changeDetectionStrategy,
|
||||||
properties: directiveAnnotation.properties,
|
properties: directiveAnnotation.properties,
|
||||||
events: directiveAnnotation.events,
|
events: directiveAnnotation.events,
|
||||||
hostListeners: hostListeners,
|
host: directiveAnnotation.host,
|
||||||
hostProperties: hostProperties,
|
lifecycleHooks: ListWrapper.filter(LIFECYCLE_HOOKS_VALUES,
|
||||||
callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, directiveType),
|
hook => hasLifecycleHook(hook, directiveType))
|
||||||
callAfterContentChecked:
|
|
||||||
hasLifecycleHook(LifecycleHooks.AfterContentChecked, directiveType),
|
|
||||||
callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, directiveType),
|
|
||||||
callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, directiveType),
|
|
||||||
callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, directiveType),
|
|
||||||
callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, directiveType),
|
|
||||||
callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, directiveType),
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
this._cache.set(directiveType, meta);
|
this._cache.set(directiveType, meta);
|
||||||
}
|
}
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewDirectivesMetadata(component: Type): cpl.DirectiveMetadata[] {
|
getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] {
|
||||||
var view = this._viewResolver.resolve(component);
|
var view = this._viewResolver.resolve(component);
|
||||||
var directives = flattenDirectives(view);
|
var directives = flattenDirectives(view);
|
||||||
for (var i = 0; i < directives.length; i++) {
|
for (var i = 0; i < directives.length; i++) {
|
||||||
@ -113,8 +87,9 @@ export class RuntimeMetadataResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDuplicatedDirectives(directives: cpl.DirectiveMetadata[]): cpl.DirectiveMetadata[] {
|
function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]):
|
||||||
var directivesMap: Map<number, cpl.DirectiveMetadata> = new Map();
|
cpl.CompileDirectiveMetadata[] {
|
||||||
|
var directivesMap: Map<number, cpl.CompileDirectiveMetadata> = new Map();
|
||||||
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
||||||
return MapWrapper.values(directivesMap);
|
return MapWrapper.values(directivesMap);
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,10 @@ export class SourceExpression {
|
|||||||
constructor(public declarations: string[], public expression: string) {}
|
constructor(public declarations: string[], public expression: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SourceExpressions {
|
||||||
|
constructor(public declarations: string[], public expressions: string[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class SourceWithImports {
|
export class SourceWithImports {
|
||||||
constructor(public source: string, public imports: string[][]) {}
|
constructor(public source: string, public imports: string[][]) {}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||||
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
@ -29,16 +29,16 @@ export class StyleCompiler {
|
|||||||
|
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||||
|
|
||||||
compileComponentRuntime(component: NormalizedDirectiveMetadata): Promise<string[]> {
|
compileComponentRuntime(component: CompileDirectiveMetadata): Promise<string[]> {
|
||||||
var styles = component.template.styles;
|
var styles = component.template.styles;
|
||||||
var styleAbsUrls = component.template.styleAbsUrls;
|
var styleAbsUrls = component.template.styleUrls;
|
||||||
return this._loadStyles(styles, styleAbsUrls,
|
return this._loadStyles(styles, styleAbsUrls,
|
||||||
component.template.encapsulation === ViewEncapsulation.Emulated)
|
component.template.encapsulation === ViewEncapsulation.Emulated)
|
||||||
.then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX,
|
.then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX,
|
||||||
`${component.type.id}`)));
|
`${component.type.id}`)));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(component: NormalizedDirectiveMetadata): SourceExpression {
|
compileComponentCodeGen(component: CompileDirectiveMetadata): SourceExpression {
|
||||||
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
var suffix;
|
var suffix;
|
||||||
if (shim) {
|
if (shim) {
|
||||||
@ -48,7 +48,7 @@ export class StyleCompiler {
|
|||||||
} else {
|
} else {
|
||||||
suffix = '';
|
suffix = '';
|
||||||
}
|
}
|
||||||
return this._styleCodeGen(component.template.styles, component.template.styleAbsUrls, shim,
|
return this._styleCodeGen(component.template.styles, component.template.styleUrls, shim,
|
||||||
suffix);
|
suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +62,8 @@ export class StyleCompiler {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearCache() { this._styleCache.clear(); }
|
||||||
|
|
||||||
private _loadStyles(plainStyles: string[], absUrls: string[],
|
private _loadStyles(plainStyles: string[], absUrls: string[],
|
||||||
encapsulate: boolean): Promise<string[]> {
|
encapsulate: boolean): Promise<string[]> {
|
||||||
var promises = absUrls.map((absUrl) => {
|
var promises = absUrls.map((absUrl) => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
import {CompileDirectiveMetadata} from './directive_metadata';
|
||||||
|
|
||||||
export interface TemplateAst {
|
export interface TemplateAst {
|
||||||
sourceInfo: string;
|
sourceInfo: string;
|
||||||
@ -38,7 +38,7 @@ export class BoundEventAst implements TemplateAst {
|
|||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitEvent(this, context);
|
return visitor.visitEvent(this, context);
|
||||||
}
|
}
|
||||||
getFullName(): string {
|
get fullName() {
|
||||||
if (isPresent(this.target)) {
|
if (isPresent(this.target)) {
|
||||||
return `${this.target}:${this.name}`;
|
return `${this.target}:${this.name}`;
|
||||||
} else {
|
} else {
|
||||||
@ -57,7 +57,7 @@ export class VariableAst implements TemplateAst {
|
|||||||
export class ElementAst implements TemplateAst {
|
export class ElementAst implements TemplateAst {
|
||||||
constructor(public name: string, public attrs: AttrAst[],
|
constructor(public name: string, public attrs: AttrAst[],
|
||||||
public properties: BoundElementPropertyAst[], public events: BoundEventAst[],
|
public properties: BoundElementPropertyAst[], public events: BoundEventAst[],
|
||||||
public vars: VariableAst[], public directives: DirectiveAst[],
|
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
|
||||||
public children: TemplateAst[], public ngContentIndex: number,
|
public children: TemplateAst[], public ngContentIndex: number,
|
||||||
public sourceInfo: string) {}
|
public sourceInfo: string) {}
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
@ -65,11 +65,11 @@ export class ElementAst implements TemplateAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isBound(): boolean {
|
isBound(): boolean {
|
||||||
return (this.properties.length > 0 || this.events.length > 0 || this.vars.length > 0 ||
|
return (this.properties.length > 0 || this.events.length > 0 || this.exportAsVars.length > 0 ||
|
||||||
this.directives.length > 0);
|
this.directives.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent(): NormalizedDirectiveMetadata {
|
getComponent(): CompileDirectiveMetadata {
|
||||||
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
||||||
this.directives[0].directive :
|
this.directives[0].directive :
|
||||||
null;
|
null;
|
||||||
@ -94,10 +94,10 @@ export class BoundDirectivePropertyAst implements TemplateAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DirectiveAst implements TemplateAst {
|
export class DirectiveAst implements TemplateAst {
|
||||||
constructor(public directive: NormalizedDirectiveMetadata,
|
constructor(public directive: CompileDirectiveMetadata,
|
||||||
public properties: BoundDirectivePropertyAst[],
|
public properties: BoundDirectivePropertyAst[],
|
||||||
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
||||||
public sourceInfo: string) {}
|
public exportAsVars: VariableAst[], public sourceInfo: string) {}
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitDirective(this, context);
|
return visitor.visitDirective(this, context);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
|
||||||
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
||||||
import {
|
import {
|
||||||
createHostComponentMeta,
|
createHostComponentMeta,
|
||||||
DirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
INormalizedDirectiveMetadata,
|
CompileTypeMetadata,
|
||||||
NormalizedDirectiveMetadata,
|
CompileTemplateMetadata
|
||||||
TypeMetadata,
|
|
||||||
ChangeDetectionMetadata,
|
|
||||||
NormalizedTemplateMetadata
|
|
||||||
} from './directive_metadata';
|
} from './directive_metadata';
|
||||||
import {TemplateAst} from './template_ast';
|
import {TemplateAst} from './template_ast';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
@ -36,7 +32,8 @@ export class TemplateCompiler {
|
|||||||
private _commandCompiler: CommandCompiler,
|
private _commandCompiler: CommandCompiler,
|
||||||
private _cdCompiler: ChangeDetectionCompiler) {}
|
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||||
|
|
||||||
normalizeDirectiveMetadata(directive: DirectiveMetadata): Promise<INormalizedDirectiveMetadata> {
|
normalizeDirectiveMetadata(directive:
|
||||||
|
CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> {
|
||||||
var normalizedTemplatePromise;
|
var normalizedTemplatePromise;
|
||||||
if (directive.isComponent) {
|
if (directive.isComponent) {
|
||||||
normalizedTemplatePromise =
|
normalizedTemplatePromise =
|
||||||
@ -45,49 +42,58 @@ export class TemplateCompiler {
|
|||||||
normalizedTemplatePromise = PromiseWrapper.resolve(null);
|
normalizedTemplatePromise = PromiseWrapper.resolve(null);
|
||||||
}
|
}
|
||||||
return normalizedTemplatePromise.then(
|
return normalizedTemplatePromise.then(
|
||||||
(normalizedTemplate) => new NormalizedDirectiveMetadata({
|
(normalizedTemplate) => new CompileDirectiveMetadata({
|
||||||
selector: directive.selector,
|
|
||||||
dynamicLoadable: directive.dynamicLoadable,
|
|
||||||
isComponent: directive.isComponent,
|
|
||||||
type: directive.type,
|
type: directive.type,
|
||||||
changeDetection: directive.changeDetection, template: normalizedTemplate
|
isComponent: directive.isComponent,
|
||||||
|
dynamicLoadable: directive.dynamicLoadable,
|
||||||
|
selector: directive.selector,
|
||||||
|
exportAs: directive.exportAs,
|
||||||
|
changeDetection: directive.changeDetection,
|
||||||
|
properties: directive.properties,
|
||||||
|
events: directive.events,
|
||||||
|
hostListeners: directive.hostListeners,
|
||||||
|
hostProperties: directive.hostProperties,
|
||||||
|
hostAttributes: directive.hostAttributes,
|
||||||
|
lifecycleHooks: directive.lifecycleHooks, template: normalizedTemplate
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeDirectiveMetadata(metadata: INormalizedDirectiveMetadata): string {
|
serializeDirectiveMetadata(metadata: CompileDirectiveMetadata): string {
|
||||||
return Json.stringify((<NormalizedDirectiveMetadata>metadata).toJson());
|
return Json.stringify(metadata.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializeDirectiveMetadata(data: string): INormalizedDirectiveMetadata {
|
deserializeDirectiveMetadata(data: string): CompileDirectiveMetadata {
|
||||||
return NormalizedDirectiveMetadata.fromJson(Json.parse(data));
|
return CompileDirectiveMetadata.fromJson(Json.parse(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
||||||
var compMeta: DirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||||
if (isBlank(compMeta) || !compMeta.isComponent || !compMeta.dynamicLoadable) {
|
var hostMeta: CompileDirectiveMetadata =
|
||||||
throw new BaseException(
|
|
||||||
`Could not compile '${stringify(type)}' because it is not dynamically loadable.`);
|
|
||||||
}
|
|
||||||
var hostMeta: NormalizedDirectiveMetadata =
|
|
||||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
||||||
return this._compiledTemplateDone.get(hostMeta.type.id);
|
return this._compiledTemplateDone.get(hostMeta.type.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileComponentRuntime(compMeta: NormalizedDirectiveMetadata,
|
clearCache() {
|
||||||
viewDirectives: DirectiveMetadata[],
|
this._styleCompiler.clearCache();
|
||||||
|
this._compiledTemplateCache.clear();
|
||||||
|
this._compiledTemplateDone.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileComponentRuntime(compMeta: CompileDirectiveMetadata,
|
||||||
|
viewDirectives: CompileDirectiveMetadata[],
|
||||||
compilingComponentIds: Set<number>): CompiledTemplate {
|
compilingComponentIds: Set<number>): CompiledTemplate {
|
||||||
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id);
|
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id);
|
||||||
var done = this._compiledTemplateDone.get(compMeta.type.id);
|
var done = this._compiledTemplateDone.get(compMeta.type.id);
|
||||||
if (isBlank(compiledTemplate)) {
|
if (isBlank(compiledTemplate)) {
|
||||||
var styles;
|
var styles;
|
||||||
var changeDetectorFactories;
|
var changeDetectorFactory;
|
||||||
var commands;
|
var commands;
|
||||||
compiledTemplate =
|
compiledTemplate =
|
||||||
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactories, commands, styles]);
|
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]);
|
||||||
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
||||||
compilingComponentIds.add(compMeta.type.id);
|
compilingComponentIds.add(compMeta.type.id);
|
||||||
done = PromiseWrapper.all([this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
done = PromiseWrapper.all([<any>this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
||||||
viewDirectives.map(
|
viewDirectives.map(
|
||||||
dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||||
@ -96,10 +102,12 @@ export class TemplateCompiler {
|
|||||||
var parsedTemplate = this._templateParser.parse(
|
var parsedTemplate = this._templateParser.parse(
|
||||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||||
|
|
||||||
changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||||
|
changeDetectorFactory = changeDetectorFactories[0];
|
||||||
styles = stylesAndNormalizedViewDirMetas[0];
|
styles = stylesAndNormalizedViewDirMetas[0];
|
||||||
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
||||||
|
changeDetectorFactories,
|
||||||
compilingComponentIds, childPromises);
|
compilingComponentIds, childPromises);
|
||||||
return PromiseWrapper.all(childPromises);
|
return PromiseWrapper.all(childPromises);
|
||||||
})
|
})
|
||||||
@ -112,12 +120,14 @@ export class TemplateCompiler {
|
|||||||
return compiledTemplate;
|
return compiledTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compileCommandsRuntime(compMeta: NormalizedDirectiveMetadata,
|
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||||
parsedTemplate: TemplateAst[], compilingComponentIds: Set<number>,
|
changeDetectorFactories: Function[],
|
||||||
|
compilingComponentIds: Set<number>,
|
||||||
childPromises: Promise<any>[]): TemplateCmd[] {
|
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||||
return this._commandCompiler.compileComponentRuntime(
|
return this._commandCompiler.compileComponentRuntime(
|
||||||
compMeta, parsedTemplate, (childComponentDir: NormalizedDirectiveMetadata) => {
|
compMeta, parsedTemplate, changeDetectorFactories,
|
||||||
var childViewDirectives: DirectiveMetadata[] =
|
(childComponentDir: CompileDirectiveMetadata) => {
|
||||||
|
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
||||||
childComponentDir.type.runtime);
|
childComponentDir.type.runtime);
|
||||||
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id);
|
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id);
|
||||||
@ -135,12 +145,12 @@ export class TemplateCompiler {
|
|||||||
components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||||
var declarations = [];
|
var declarations = [];
|
||||||
var templateArguments = [];
|
var templateArguments = [];
|
||||||
var componentMetas: NormalizedDirectiveMetadata[] = [];
|
var componentMetas: CompileDirectiveMetadata[] = [];
|
||||||
components.forEach(componentWithDirs => {
|
components.forEach(componentWithDirs => {
|
||||||
var compMeta = <NormalizedDirectiveMetadata>componentWithDirs.component;
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||||
componentMetas.push(compMeta);
|
componentMetas.push(compMeta);
|
||||||
this._processTemplateCodeGen(compMeta,
|
this._processTemplateCodeGen(compMeta,
|
||||||
<NormalizedDirectiveMetadata[]>componentWithDirs.directives,
|
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
||||||
declarations, templateArguments);
|
declarations, templateArguments);
|
||||||
if (compMeta.dynamicLoadable) {
|
if (compMeta.dynamicLoadable) {
|
||||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
@ -148,7 +158,7 @@ export class TemplateCompiler {
|
|||||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: NormalizedDirectiveMetadata,
|
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
|
||||||
index: number) => {
|
index: number) => {
|
||||||
var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`);
|
var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`);
|
||||||
declarations.push(
|
declarations.push(
|
||||||
@ -161,32 +171,33 @@ export class TemplateCompiler {
|
|||||||
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
|
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processTemplateCodeGen(compMeta: NormalizedDirectiveMetadata,
|
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
||||||
directives: NormalizedDirectiveMetadata[],
|
directives: CompileDirectiveMetadata[],
|
||||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
||||||
var parsedTemplate =
|
var parsedTemplate =
|
||||||
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||||
var changeDetectorsExpr = this._cdCompiler.compileComponentCodeGen(
|
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||||
compMeta, parsedTemplate, codeGenComponentTemplateFactory);
|
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
||||||
|
codeGenComponentTemplateFactory);
|
||||||
|
|
||||||
addAll(styleExpr.declarations, targetDeclarations);
|
addAll(styleExpr.declarations, targetDeclarations);
|
||||||
addAll(changeDetectorsExpr.declarations, targetDeclarations);
|
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
||||||
addAll(commandsExpr.declarations, targetDeclarations);
|
addAll(commandsExpr.declarations, targetDeclarations);
|
||||||
|
|
||||||
targetTemplateArguments.push(
|
targetTemplateArguments.push(
|
||||||
[changeDetectorsExpr.expression, commandsExpr.expression, styleExpr.expression]);
|
[changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NormalizedComponentWithViewDirectives {
|
export class NormalizedComponentWithViewDirectives {
|
||||||
constructor(public component: INormalizedDirectiveMetadata,
|
constructor(public component: CompileDirectiveMetadata,
|
||||||
public directives: INormalizedDirectiveMetadata[]) {}
|
public directives: CompileDirectiveMetadata[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateVariableName(type: TypeMetadata): string {
|
function templateVariableName(type: CompileTypeMetadata): string {
|
||||||
return `${type.name}Template`;
|
return `${type.name}Template`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +211,6 @@ function addAll(source: any[], target: any[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function codeGenComponentTemplateFactory(nestedCompType: NormalizedDirectiveMetadata): string {
|
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
||||||
return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`;
|
return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
TemplateMetadata,
|
CompileDirectiveMetadata,
|
||||||
NormalizedDirectiveMetadata,
|
CompileTemplateMetadata
|
||||||
NormalizedTemplateMetadata
|
|
||||||
} from './directive_metadata';
|
} from './directive_metadata';
|
||||||
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
@ -11,6 +10,7 @@ import {XHR} from 'angular2/src/core/render/xhr';
|
|||||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||||
import {resolveStyleUrls} from './style_url_resolver';
|
import {resolveStyleUrls} from './style_url_resolver';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HtmlAstVisitor,
|
HtmlAstVisitor,
|
||||||
@ -22,21 +22,15 @@ import {
|
|||||||
} from './html_ast';
|
} from './html_ast';
|
||||||
import {HtmlParser} from './html_parser';
|
import {HtmlParser} from './html_parser';
|
||||||
|
|
||||||
const NG_CONTENT_SELECT_ATTR = 'select';
|
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
||||||
const NG_CONTENT_ELEMENT = 'ng-content';
|
|
||||||
const LINK_ELEMENT = 'link';
|
|
||||||
const LINK_STYLE_REL_ATTR = 'rel';
|
|
||||||
const LINK_STYLE_HREF_ATTR = 'href';
|
|
||||||
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
|
||||||
const STYLE_ELEMENT = 'style';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TemplateNormalizer {
|
export class TemplateNormalizer {
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
||||||
private _domParser: HtmlParser) {}
|
private _domParser: HtmlParser) {}
|
||||||
|
|
||||||
normalizeTemplate(directiveType: TypeMetadata,
|
normalizeTemplate(directiveType: CompileTypeMetadata,
|
||||||
template: TemplateMetadata): Promise<NormalizedTemplateMetadata> {
|
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
|
||||||
if (isPresent(template.template)) {
|
if (isPresent(template.template)) {
|
||||||
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
||||||
directiveType, template, template.template, directiveType.moduleId));
|
directiveType, template, template.template, directiveType.moduleId));
|
||||||
@ -48,11 +42,11 @@ export class TemplateNormalizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeLoadedTemplate(directiveType: TypeMetadata, templateMeta: TemplateMetadata,
|
normalizeLoadedTemplate(directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata,
|
||||||
template: string, templateAbsUrl: string): NormalizedTemplateMetadata {
|
template: string, templateAbsUrl: string): CompileTemplateMetadata {
|
||||||
var domNodes = this._domParser.parse(template, directiveType.name);
|
var domNodes = this._domParser.parse(template, directiveType.name);
|
||||||
var visitor = new TemplatePreparseVisitor();
|
var visitor = new TemplatePreparseVisitor();
|
||||||
var remainingNodes = htmlVisitAll(visitor, domNodes);
|
htmlVisitAll(visitor, domNodes);
|
||||||
var allStyles = templateMeta.styles.concat(visitor.styles);
|
var allStyles = templateMeta.styles.concat(visitor.styles);
|
||||||
|
|
||||||
var allStyleAbsUrls =
|
var allStyleAbsUrls =
|
||||||
@ -65,11 +59,17 @@ export class TemplateNormalizer {
|
|||||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
||||||
return styleWithImports.style;
|
return styleWithImports.style;
|
||||||
});
|
});
|
||||||
return new NormalizedTemplateMetadata({
|
var encapsulation = templateMeta.encapsulation;
|
||||||
encapsulation: templateMeta.encapsulation,
|
if (encapsulation === ViewEncapsulation.Emulated && allResolvedStyles.length === 0 &&
|
||||||
template: this._domParser.unparse(remainingNodes),
|
allStyleAbsUrls.length === 0) {
|
||||||
|
encapsulation = ViewEncapsulation.None;
|
||||||
|
}
|
||||||
|
return new CompileTemplateMetadata({
|
||||||
|
encapsulation: encapsulation,
|
||||||
|
template: template,
|
||||||
|
templateUrl: templateAbsUrl,
|
||||||
styles: allResolvedStyles,
|
styles: allResolvedStyles,
|
||||||
styleAbsUrls: allStyleAbsUrls,
|
styleUrls: allStyleAbsUrls,
|
||||||
ngContentSelectors: visitor.ngContentSelectors
|
ngContentSelectors: visitor.ngContentSelectors
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -80,25 +80,13 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
|||||||
styles: string[] = [];
|
styles: string[] = [];
|
||||||
styleUrls: string[] = [];
|
styleUrls: string[] = [];
|
||||||
|
|
||||||
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
|
visitElement(ast: HtmlElementAst, context: any): any {
|
||||||
var selectAttr = null;
|
var preparsedElement = preparseElement(ast);
|
||||||
var hrefAttr = null;
|
switch (preparsedElement.type) {
|
||||||
var relAttr = null;
|
case PreparsedElementType.NG_CONTENT:
|
||||||
ast.attrs.forEach(attr => {
|
this.ngContentSelectors.push(preparsedElement.selectAttr);
|
||||||
if (attr.name == NG_CONTENT_SELECT_ATTR) {
|
break;
|
||||||
selectAttr = attr.value;
|
case PreparsedElementType.STYLE:
|
||||||
} else if (attr.name == LINK_STYLE_HREF_ATTR) {
|
|
||||||
hrefAttr = attr.value;
|
|
||||||
} else if (attr.name == LINK_STYLE_REL_ATTR) {
|
|
||||||
relAttr = attr.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var nodeName = ast.name;
|
|
||||||
var keepElement = true;
|
|
||||||
if (nodeName == NG_CONTENT_ELEMENT) {
|
|
||||||
this.ngContentSelectors.push(normalizeNgContentSelect(selectAttr));
|
|
||||||
} else if (nodeName == STYLE_ELEMENT) {
|
|
||||||
keepElement = false;
|
|
||||||
var textContent = '';
|
var textContent = '';
|
||||||
ast.children.forEach(child => {
|
ast.children.forEach(child => {
|
||||||
if (child instanceof HtmlTextAst) {
|
if (child instanceof HtmlTextAst) {
|
||||||
@ -106,24 +94,16 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.styles.push(textContent);
|
this.styles.push(textContent);
|
||||||
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
break;
|
||||||
keepElement = false;
|
case PreparsedElementType.STYLESHEET:
|
||||||
this.styleUrls.push(hrefAttr);
|
this.styleUrls.push(preparsedElement.hrefAttr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (preparsedElement.type !== PreparsedElementType.NON_BINDABLE) {
|
||||||
|
htmlVisitAll(this, ast.children);
|
||||||
}
|
}
|
||||||
if (keepElement) {
|
|
||||||
return new HtmlElementAst(ast.name, ast.attrs, htmlVisitAll(this, ast.children),
|
|
||||||
ast.sourceInfo);
|
|
||||||
} else {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
|
||||||
visitAttr(ast: HtmlAttrAst, context: any): HtmlAttrAst { return ast; }
|
visitText(ast: HtmlTextAst, context: any): any { return null; }
|
||||||
visitText(ast: HtmlTextAst, context: any): HtmlTextAst { return ast; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeNgContentSelect(selectAttr: string): string {
|
|
||||||
if (isBlank(selectAttr) || selectAttr.length === 0) {
|
|
||||||
return '*';
|
|
||||||
}
|
|
||||||
return selectAttr;
|
|
||||||
}
|
}
|
@ -1,4 +1,9 @@
|
|||||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {
|
||||||
|
MapWrapper,
|
||||||
|
ListWrapper,
|
||||||
|
StringMapWrapper,
|
||||||
|
SetWrapper
|
||||||
|
} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
RegExpWrapper,
|
RegExpWrapper,
|
||||||
isPresent,
|
isPresent,
|
||||||
@ -12,7 +17,7 @@ import {Injectable} from 'angular2/src/core/di';
|
|||||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
||||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
import {CompileDirectiveMetadata} from './directive_metadata';
|
||||||
import {HtmlParser} from './html_parser';
|
import {HtmlParser} from './html_parser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -33,6 +38,7 @@ import {
|
|||||||
import {CssSelector, SelectorMatcher} from 'angular2/src/core/render/dom/compiler/selector';
|
import {CssSelector, SelectorMatcher} from 'angular2/src/core/render/dom/compiler/selector';
|
||||||
|
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||||
|
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HtmlAstVisitor,
|
HtmlAstVisitor,
|
||||||
@ -43,7 +49,7 @@ import {
|
|||||||
htmlVisitAll
|
htmlVisitAll
|
||||||
} from './html_ast';
|
} from './html_ast';
|
||||||
|
|
||||||
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
import {dashCaseToCamelCase, camelCaseToDashCase, splitAtColon} from './util';
|
||||||
|
|
||||||
// Group 1 = "bind-"
|
// Group 1 = "bind-"
|
||||||
// Group 2 = "var-" or "#"
|
// Group 2 = "var-" or "#"
|
||||||
@ -56,12 +62,10 @@ import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
|||||||
var BIND_NAME_REGEXP =
|
var BIND_NAME_REGEXP =
|
||||||
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||||
|
|
||||||
const NG_CONTENT_ELEMENT = 'ng-content';
|
|
||||||
const TEMPLATE_ELEMENT = 'template';
|
const TEMPLATE_ELEMENT = 'template';
|
||||||
const TEMPLATE_ATTR = 'template';
|
const TEMPLATE_ATTR = 'template';
|
||||||
const TEMPLATE_ATTR_PREFIX = '*';
|
const TEMPLATE_ATTR_PREFIX = '*';
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
const IMPLICIT_VAR_NAME = '$implicit';
|
|
||||||
|
|
||||||
var PROPERTY_PARTS_SEPARATOR = new RegExp('\\.');
|
var PROPERTY_PARTS_SEPARATOR = new RegExp('\\.');
|
||||||
const ATTRIBUTE_PREFIX = 'attr';
|
const ATTRIBUTE_PREFIX = 'attr';
|
||||||
@ -75,7 +79,7 @@ export class TemplateParser {
|
|||||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _htmlParser: HtmlParser) {}
|
private _htmlParser: HtmlParser) {}
|
||||||
|
|
||||||
parse(template: string, directives: NormalizedDirectiveMetadata[],
|
parse(template: string, directives: CompileDirectiveMetadata[],
|
||||||
sourceInfo: string): TemplateAst[] {
|
sourceInfo: string): TemplateAst[] {
|
||||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||||
var result =
|
var result =
|
||||||
@ -91,12 +95,15 @@ export class TemplateParser {
|
|||||||
class TemplateParseVisitor implements HtmlAstVisitor {
|
class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
selectorMatcher: SelectorMatcher;
|
selectorMatcher: SelectorMatcher;
|
||||||
errors: string[] = [];
|
errors: string[] = [];
|
||||||
constructor(directives: NormalizedDirectiveMetadata[], private _exprParser: Parser,
|
directivesIndexByTypeId: Map<number, number> = new Map();
|
||||||
|
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
|
||||||
private _schemaRegistry: ElementSchemaRegistry) {
|
private _schemaRegistry: ElementSchemaRegistry) {
|
||||||
this.selectorMatcher = new SelectorMatcher();
|
this.selectorMatcher = new SelectorMatcher();
|
||||||
directives.forEach(directive => {
|
ListWrapper.forEachWithIndex(directives,
|
||||||
|
(directive: CompileDirectiveMetadata, index: number) => {
|
||||||
var selector = CssSelector.parse(directive.selector);
|
var selector = CssSelector.parse(directive.selector);
|
||||||
this.selectorMatcher.addSelectables(selector, directive);
|
this.selectorMatcher.addSelectables(selector, directive);
|
||||||
|
this.directivesIndexByTypeId.set(directive.type.id, index);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +161,17 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
|
|
||||||
visitElement(element: HtmlElementAst, component: Component): any {
|
visitElement(element: HtmlElementAst, component: Component): any {
|
||||||
var nodeName = element.name;
|
var nodeName = element.name;
|
||||||
|
var preparsedElement = preparseElement(element);
|
||||||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
|
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||||
|
preparsedElement.type === PreparsedElementType.STYLESHEET ||
|
||||||
|
preparsedElement.type === PreparsedElementType.NON_BINDABLE) {
|
||||||
|
// Skipping <script> for security reasons
|
||||||
|
// Skipping <style> and stylesheets as we already processed them
|
||||||
|
// in the StyleCompiler
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var matchableAttrs: string[][] = [];
|
var matchableAttrs: string[][] = [];
|
||||||
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||||
var vars: VariableAst[] = [];
|
var vars: VariableAst[] = [];
|
||||||
@ -177,26 +195,29 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
hasInlineTemplates = true;
|
hasInlineTemplates = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
var isTemplateElement = nodeName == TEMPLATE_ELEMENT;
|
||||||
var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs);
|
var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs);
|
||||||
var directives = this._createDirectiveAsts(
|
var directives = this._createDirectiveAsts(
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
||||||
elementOrDirectiveProps, element.sourceInfo);
|
elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceInfo);
|
||||||
var elementProps: BoundElementPropertyAst[] =
|
var elementProps: BoundElementPropertyAst[] =
|
||||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
||||||
var children = htmlVisitAll(this, element.children, Component.create(directives));
|
var children = htmlVisitAll(this, element.children, Component.create(directives));
|
||||||
var elementNgContentIndex =
|
var elementNgContentIndex =
|
||||||
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
|
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
|
||||||
var parsedElement;
|
var parsedElement;
|
||||||
if (nodeName == NG_CONTENT_ELEMENT) {
|
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||||
parsedElement = new NgContentAst(elementNgContentIndex, element.sourceInfo);
|
parsedElement = new NgContentAst(elementNgContentIndex, element.sourceInfo);
|
||||||
} else if (nodeName == TEMPLATE_ELEMENT) {
|
} else if (isTemplateElement) {
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, events,
|
this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, events,
|
||||||
element.sourceInfo);
|
element.sourceInfo);
|
||||||
parsedElement = new EmbeddedTemplateAst(attrs, vars, directives, children,
|
parsedElement = new EmbeddedTemplateAst(attrs, vars, directives, children,
|
||||||
elementNgContentIndex, element.sourceInfo);
|
elementNgContentIndex, element.sourceInfo);
|
||||||
} else {
|
} else {
|
||||||
this._assertOnlyOneComponent(directives, element.sourceInfo);
|
this._assertOnlyOneComponent(directives, element.sourceInfo);
|
||||||
parsedElement = new ElementAst(nodeName, attrs, elementProps, events, vars, directives,
|
var elementExportAsVars = ListWrapper.filter(vars, varAst => varAst.value.length === 0);
|
||||||
|
parsedElement =
|
||||||
|
new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives,
|
||||||
children, elementNgContentIndex, element.sourceInfo);
|
children, elementNgContentIndex, element.sourceInfo);
|
||||||
}
|
}
|
||||||
if (hasInlineTemplates) {
|
if (hasInlineTemplates) {
|
||||||
@ -204,7 +225,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||||
var templateDirectives = this._createDirectiveAsts(
|
var templateDirectives = this._createDirectiveAsts(
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
||||||
templateElementOrDirectiveProps, element.sourceInfo);
|
templateElementOrDirectiveProps, [], element.sourceInfo);
|
||||||
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||||
element.name, templateElementOrDirectiveProps, templateDirectives);
|
element.name, templateElementOrDirectiveProps, templateDirectives);
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps,
|
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps,
|
||||||
@ -263,8 +284,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
} else if (isPresent(
|
} else if (isPresent(
|
||||||
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
||||||
var identifier = bindParts[5];
|
var identifier = bindParts[5];
|
||||||
var value = attrValue.length === 0 ? IMPLICIT_VAR_NAME : attrValue;
|
this._parseVariable(identifier, attrValue, attr.sourceInfo, targetMatchableAttrs,
|
||||||
this._parseVariable(identifier, value, attr.sourceInfo, targetMatchableAttrs, targetVars);
|
targetVars);
|
||||||
|
|
||||||
} else if (isPresent(bindParts[3])) { // match: on-event
|
} else if (isPresent(bindParts[3])) { // match: on-event
|
||||||
this._parseEvent(bindParts[5], attrValue, attr.sourceInfo, targetMatchableAttrs,
|
this._parseEvent(bindParts[5], attrValue, attr.sourceInfo, targetMatchableAttrs,
|
||||||
@ -377,7 +398,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||||
elementCssSelector: CssSelector): NormalizedDirectiveMetadata[] {
|
elementCssSelector: CssSelector): CompileDirectiveMetadata[] {
|
||||||
var directives = [];
|
var directives = [];
|
||||||
selectorMatcher.match(elementCssSelector,
|
selectorMatcher.match(elementCssSelector,
|
||||||
(selector, directive) => { directives.push(directive); });
|
(selector, directive) => { directives.push(directive); });
|
||||||
@ -385,7 +406,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
// as selectorMatcher uses Maps inside.
|
// as selectorMatcher uses Maps inside.
|
||||||
// Also need to make components the first directive in the array
|
// Also need to make components the first directive in the array
|
||||||
ListWrapper.sort(directives,
|
ListWrapper.sort(directives,
|
||||||
(dir1: NormalizedDirectiveMetadata, dir2: NormalizedDirectiveMetadata) => {
|
(dir1: CompileDirectiveMetadata, dir2: CompileDirectiveMetadata) => {
|
||||||
var dir1Comp = dir1.isComponent;
|
var dir1Comp = dir1.isComponent;
|
||||||
var dir2Comp = dir2.isComponent;
|
var dir2Comp = dir2.isComponent;
|
||||||
if (dir1Comp && !dir2Comp) {
|
if (dir1Comp && !dir2Comp) {
|
||||||
@ -393,29 +414,44 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
} else if (!dir1Comp && dir2Comp) {
|
} else if (!dir1Comp && dir2Comp) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return StringWrapper.compare(dir1.type.name, dir2.type.name);
|
return this.directivesIndexByTypeId.get(dir1.type.id) -
|
||||||
|
this.directivesIndexByTypeId.get(dir2.type.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return directives;
|
return directives;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectiveAsts(elementName: string, directives: NormalizedDirectiveMetadata[],
|
private _createDirectiveAsts(elementName: string, directives: CompileDirectiveMetadata[],
|
||||||
props: BoundElementOrDirectiveProperty[],
|
props: BoundElementOrDirectiveProperty[],
|
||||||
|
possibleExportAsVars: VariableAst[],
|
||||||
sourceInfo: string): DirectiveAst[] {
|
sourceInfo: string): DirectiveAst[] {
|
||||||
return directives.map((directive: NormalizedDirectiveMetadata) => {
|
var matchedVariables: Set<string> = new Set();
|
||||||
|
var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => {
|
||||||
var hostProperties: BoundElementPropertyAst[] = [];
|
var hostProperties: BoundElementPropertyAst[] = [];
|
||||||
var hostEvents: BoundEventAst[] = [];
|
var hostEvents: BoundEventAst[] = [];
|
||||||
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||||
var changeDetection = directive.changeDetection;
|
this._createDirectiveHostPropertyAsts(elementName, directive.hostProperties, sourceInfo,
|
||||||
if (isPresent(changeDetection)) {
|
hostProperties);
|
||||||
this._createDirectiveHostPropertyAsts(elementName, changeDetection.hostProperties,
|
this._createDirectiveHostEventAsts(directive.hostListeners, sourceInfo, hostEvents);
|
||||||
sourceInfo, hostProperties);
|
this._createDirectivePropertyAsts(directive.properties, props, directiveProperties);
|
||||||
this._createDirectiveHostEventAsts(changeDetection.hostListeners, sourceInfo, hostEvents);
|
var exportAsVars = [];
|
||||||
this._createDirectivePropertyAsts(changeDetection.properties, props, directiveProperties);
|
possibleExportAsVars.forEach((varAst) => {
|
||||||
|
if ((varAst.value.length === 0 && directive.isComponent) ||
|
||||||
|
(directive.exportAs == varAst.value)) {
|
||||||
|
exportAsVars.push(varAst);
|
||||||
|
matchedVariables.add(varAst.name);
|
||||||
}
|
}
|
||||||
return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents,
|
|
||||||
sourceInfo);
|
|
||||||
});
|
});
|
||||||
|
return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents,
|
||||||
|
exportAsVars, sourceInfo);
|
||||||
|
});
|
||||||
|
possibleExportAsVars.forEach((varAst) => {
|
||||||
|
if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) {
|
||||||
|
this._reportError(
|
||||||
|
`There is no directive with "exportAs" set to "${varAst.value}" at ${varAst.sourceInfo}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return directiveAsts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectiveHostPropertyAsts(elementName: string,
|
private _createDirectiveHostPropertyAsts(elementName: string,
|
||||||
@ -439,7 +475,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectivePropertyAsts(directiveProperties: string[],
|
private _createDirectivePropertyAsts(directiveProperties: StringMap<string, string>,
|
||||||
boundProps: BoundElementOrDirectiveProperty[],
|
boundProps: BoundElementOrDirectiveProperty[],
|
||||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
||||||
if (isPresent(directiveProperties)) {
|
if (isPresent(directiveProperties)) {
|
||||||
@ -447,12 +483,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
boundProps.forEach(boundProp =>
|
boundProps.forEach(boundProp =>
|
||||||
boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp));
|
boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp));
|
||||||
|
|
||||||
directiveProperties.forEach((bindConfig: string) => {
|
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
|
||||||
// canonical syntax: `dirProp: elProp`
|
elProp = dashCaseToCamelCase(elProp);
|
||||||
// if there is no `:`, use dirProp = elProp
|
|
||||||
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
|
||||||
var dirProp = parts[0];
|
|
||||||
var elProp = dashCaseToCamelCase(parts[1]);
|
|
||||||
var boundProp = boundPropsByName.get(elProp);
|
var boundProp = boundPropsByName.get(elProp);
|
||||||
|
|
||||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||||
@ -566,15 +598,6 @@ export function splitClasses(classAttrValue: string): string[] {
|
|||||||
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
|
||||||
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
|
||||||
if (parts.length > 1) {
|
|
||||||
return parts;
|
|
||||||
} else {
|
|
||||||
return defaultValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Component {
|
class Component {
|
||||||
static create(directives: DirectiveAst[]): Component {
|
static create(directives: DirectiveAst[]): Component {
|
||||||
if (directives.length === 0 || !directives[0].directive.isComponent) {
|
if (directives.length === 0 || !directives[0].directive.isComponent) {
|
||||||
|
67
modules/angular2/src/compiler/template_preparser.ts
Normal file
67
modules/angular2/src/compiler/template_preparser.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import {HtmlElementAst} from './html_ast';
|
||||||
|
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
|
const NG_CONTENT_SELECT_ATTR = 'select';
|
||||||
|
const NG_CONTENT_ELEMENT = 'ng-content';
|
||||||
|
const LINK_ELEMENT = 'link';
|
||||||
|
const LINK_STYLE_REL_ATTR = 'rel';
|
||||||
|
const LINK_STYLE_HREF_ATTR = 'href';
|
||||||
|
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
||||||
|
const STYLE_ELEMENT = 'style';
|
||||||
|
const SCRIPT_ELEMENT = 'script';
|
||||||
|
const NG_NON_BINDABLE_ATTR = 'ng-non-bindable';
|
||||||
|
|
||||||
|
export function preparseElement(ast: HtmlElementAst): PreparsedElement {
|
||||||
|
var selectAttr = null;
|
||||||
|
var hrefAttr = null;
|
||||||
|
var relAttr = null;
|
||||||
|
var nonBindable = false;
|
||||||
|
ast.attrs.forEach(attr => {
|
||||||
|
if (attr.name == NG_CONTENT_SELECT_ATTR) {
|
||||||
|
selectAttr = attr.value;
|
||||||
|
} else if (attr.name == LINK_STYLE_HREF_ATTR) {
|
||||||
|
hrefAttr = attr.value;
|
||||||
|
} else if (attr.name == LINK_STYLE_REL_ATTR) {
|
||||||
|
relAttr = attr.value;
|
||||||
|
} else if (attr.name == NG_NON_BINDABLE_ATTR) {
|
||||||
|
nonBindable = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selectAttr = normalizeNgContentSelect(selectAttr);
|
||||||
|
var nodeName = ast.name;
|
||||||
|
var type = PreparsedElementType.OTHER;
|
||||||
|
if (nonBindable) {
|
||||||
|
type = PreparsedElementType.NON_BINDABLE;
|
||||||
|
} else if (nodeName == NG_CONTENT_ELEMENT) {
|
||||||
|
type = PreparsedElementType.NG_CONTENT;
|
||||||
|
} else if (nodeName == STYLE_ELEMENT) {
|
||||||
|
type = PreparsedElementType.STYLE;
|
||||||
|
} else if (nodeName == SCRIPT_ELEMENT) {
|
||||||
|
type = PreparsedElementType.SCRIPT;
|
||||||
|
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
||||||
|
type = PreparsedElementType.STYLESHEET;
|
||||||
|
}
|
||||||
|
return new PreparsedElement(type, selectAttr, hrefAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PreparsedElementType {
|
||||||
|
NG_CONTENT,
|
||||||
|
STYLE,
|
||||||
|
STYLESHEET,
|
||||||
|
SCRIPT,
|
||||||
|
NON_BINDABLE,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PreparsedElement {
|
||||||
|
constructor(public type: PreparsedElementType, public selectAttr: string,
|
||||||
|
public hrefAttr: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function normalizeNgContentSelect(selectAttr: string): string {
|
||||||
|
if (isBlank(selectAttr) || selectAttr.length === 0) {
|
||||||
|
return '*';
|
||||||
|
}
|
||||||
|
return selectAttr;
|
||||||
|
}
|
@ -72,3 +72,13 @@ export function codeGenValueFn(params: string[], value: string): string {
|
|||||||
return `function(${params.join(',')}) { return ${value}; }`;
|
return `function(${params.join(',')}) { return ${value}; }`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
|
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return parts;
|
||||||
|
} else {
|
||||||
|
return defaultValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -42,7 +42,7 @@ export {
|
|||||||
DebugContext,
|
DebugContext,
|
||||||
ChangeDetectorGenConfig
|
ChangeDetectorGenConfig
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
export {ChangeDetectionStrategy, changeDetectionStrategyFromJson} from './constants';
|
export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants';
|
||||||
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||||
export {BindingRecord, BindingTarget} from './binding_record';
|
export {BindingRecord, BindingTarget} from './binding_record';
|
||||||
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
import {
|
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/core/facade/lang';
|
||||||
StringWrapper,
|
|
||||||
normalizeBool,
|
|
||||||
isBlank,
|
|
||||||
serializeEnum,
|
|
||||||
deserializeEnum
|
|
||||||
} from 'angular2/src/core/facade/lang';
|
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
|
||||||
|
|
||||||
export enum ChangeDetectionStrategy {
|
export enum ChangeDetectionStrategy {
|
||||||
/**
|
/**
|
||||||
@ -48,19 +41,15 @@ export enum ChangeDetectionStrategy {
|
|||||||
OnPushObserve
|
OnPushObserve
|
||||||
}
|
}
|
||||||
|
|
||||||
var strategyMap: Map<number, ChangeDetectionStrategy> = MapWrapper.createFromPairs([
|
export var CHANGE_DECTION_STRATEGY_VALUES = [
|
||||||
[0, ChangeDetectionStrategy.CheckOnce],
|
ChangeDetectionStrategy.CheckOnce,
|
||||||
[1, ChangeDetectionStrategy.Checked],
|
ChangeDetectionStrategy.Checked,
|
||||||
[2, ChangeDetectionStrategy.CheckAlways],
|
ChangeDetectionStrategy.CheckAlways,
|
||||||
[3, ChangeDetectionStrategy.Detached],
|
ChangeDetectionStrategy.Detached,
|
||||||
[4, ChangeDetectionStrategy.OnPush],
|
ChangeDetectionStrategy.OnPush,
|
||||||
[5, ChangeDetectionStrategy.Default],
|
ChangeDetectionStrategy.Default,
|
||||||
[6, ChangeDetectionStrategy.OnPushObserve]
|
ChangeDetectionStrategy.OnPushObserve
|
||||||
]);
|
];
|
||||||
|
|
||||||
export function changeDetectionStrategyFromJson(value: number): ChangeDetectionStrategy {
|
|
||||||
return deserializeEnum(value, strategyMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: ChangeDetectionStrategy):
|
export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: ChangeDetectionStrategy):
|
||||||
boolean {
|
boolean {
|
||||||
|
@ -117,7 +117,6 @@ export class DirectiveResolver {
|
|||||||
properties: mergedProperties,
|
properties: mergedProperties,
|
||||||
events: mergedEvents,
|
events: mergedEvents,
|
||||||
host: mergedHost,
|
host: mergedHost,
|
||||||
dynamicLoadable: dm.dynamicLoadable,
|
|
||||||
bindings: dm.bindings,
|
bindings: dm.bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
moduleId: dm.moduleId,
|
moduleId: dm.moduleId,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {StringMap} from 'angular2/src/core/facade/collection';
|
import {StringMap, MapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
|
||||||
export enum LifecycleHooks {
|
export enum LifecycleHooks {
|
||||||
OnInit,
|
OnInit,
|
||||||
@ -11,6 +11,17 @@ export enum LifecycleHooks {
|
|||||||
AfterViewChecked
|
AfterViewChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export var LIFECYCLE_HOOKS_VALUES = [
|
||||||
|
LifecycleHooks.OnInit,
|
||||||
|
LifecycleHooks.OnDestroy,
|
||||||
|
LifecycleHooks.DoCheck,
|
||||||
|
LifecycleHooks.OnChanges,
|
||||||
|
LifecycleHooks.AfterContentInit,
|
||||||
|
LifecycleHooks.AfterContentChecked,
|
||||||
|
LifecycleHooks.AfterViewInit,
|
||||||
|
LifecycleHooks.AfterViewChecked
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lifecycle hooks are guaranteed to be called in the following order:
|
* Lifecycle hooks are guaranteed to be called in the following order:
|
||||||
* - `OnChanges` (if any bindings have changed),
|
* - `OnChanges` (if any bindings have changed),
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from 'angular2/src/core/render/render';
|
} from 'angular2/src/core/render/render';
|
||||||
|
|
||||||
export class CompiledTemplate {
|
export class CompiledTemplate {
|
||||||
private _changeDetectorFactories: Function[] = null;
|
private _changeDetectorFactory: Function = null;
|
||||||
private _styles: string[] = null;
|
private _styles: string[] = null;
|
||||||
private _commands: TemplateCmd[] = null;
|
private _commands: TemplateCmd[] = null;
|
||||||
// Note: paramGetter is a function so that we can have cycles between templates!
|
// Note: paramGetter is a function so that we can have cycles between templates!
|
||||||
@ -19,15 +19,15 @@ export class CompiledTemplate {
|
|||||||
private _init() {
|
private _init() {
|
||||||
if (isBlank(this._commands)) {
|
if (isBlank(this._commands)) {
|
||||||
var params = this._paramGetter();
|
var params = this._paramGetter();
|
||||||
this._changeDetectorFactories = params[0];
|
this._changeDetectorFactory = params[0];
|
||||||
this._commands = params[1];
|
this._commands = params[1];
|
||||||
this._styles = params[2];
|
this._styles = params[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get changeDetectorFactories(): Function[] {
|
get changeDetectorFactory(): Function {
|
||||||
this._init();
|
this._init();
|
||||||
return this._changeDetectorFactories;
|
return this._changeDetectorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
get styles(): string[] {
|
get styles(): string[] {
|
||||||
@ -69,26 +69,28 @@ export function ngContent(ngContentIndex: number): NgContentCmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
|
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
|
||||||
variableNameAndValues: string[];
|
variableNameAndValues: Array<string | number>;
|
||||||
eventNames: string[];
|
eventTargetAndNames: string[];
|
||||||
directives: Type[];
|
directives: Type[];
|
||||||
visit(visitor: CommandVisitor, context: any): any;
|
visit(visitor: CommandVisitor, context: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
||||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
constructor(public name: string, public attrNameAndValues: string[],
|
||||||
public variableNameAndValues: string[], public directives: Type[],
|
public eventTargetAndNames: string[],
|
||||||
|
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||||
public isBound: boolean, public ngContentIndex: number) {}
|
public isBound: boolean, public ngContentIndex: number) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: CommandVisitor, context: any): any {
|
||||||
return visitor.visitBeginElement(this, context);
|
return visitor.visitBeginElement(this, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function beginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
export function beginElement(name: string, attrNameAndValues: string[],
|
||||||
variableNameAndValues: string[], directives: Type[], isBound: boolean,
|
eventTargetAndNames: string[],
|
||||||
ngContentIndex: number): BeginElementCmd {
|
variableNameAndValues: Array<string | number>, directives: Type[],
|
||||||
return new BeginElementCmd(name, attrNameAndValues, eventNames, variableNameAndValues, directives,
|
isBound: boolean, ngContentIndex: number): BeginElementCmd {
|
||||||
isBound, ngContentIndex);
|
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||||
|
directives, isBound, ngContentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EndElementCmd implements TemplateCmd {
|
export class EndElementCmd implements TemplateCmd {
|
||||||
@ -103,8 +105,9 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
|
|||||||
isBound: boolean = true;
|
isBound: boolean = true;
|
||||||
templateId: number;
|
templateId: number;
|
||||||
component: Type;
|
component: Type;
|
||||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
constructor(public name: string, public attrNameAndValues: string[],
|
||||||
public variableNameAndValues: string[], public directives: Type[],
|
public eventTargetAndNames: string[],
|
||||||
|
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||||
public nativeShadow: boolean, public ngContentIndex: number,
|
public nativeShadow: boolean, public ngContentIndex: number,
|
||||||
public template: CompiledTemplate) {
|
public template: CompiledTemplate) {
|
||||||
this.component = directives[0];
|
this.component = directives[0];
|
||||||
@ -115,11 +118,11 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function beginComponent(name: string, attrNameAnsValues: string[], eventNames: string[],
|
export function beginComponent(
|
||||||
variableNameAndValues: string[], directives: Type[],
|
name: string, attrNameAnsValues: string[], eventTargetAndNames: string[],
|
||||||
nativeShadow: boolean, ngContentIndex: number,
|
variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean,
|
||||||
template: CompiledTemplate): BeginComponentCmd {
|
ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd {
|
||||||
return new BeginComponentCmd(name, attrNameAnsValues, eventNames, variableNameAndValues,
|
return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues,
|
||||||
directives, nativeShadow, ngContentIndex, template);
|
directives, nativeShadow, ngContentIndex, template);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +138,10 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
|||||||
RenderEmbeddedTemplateCmd {
|
RenderEmbeddedTemplateCmd {
|
||||||
isBound: boolean = true;
|
isBound: boolean = true;
|
||||||
name: string = null;
|
name: string = null;
|
||||||
eventNames: string[] = EMPTY_ARR;
|
eventTargetAndNames: string[] = EMPTY_ARR;
|
||||||
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
||||||
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
||||||
public children: TemplateCmd[]) {}
|
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
|
||||||
visit(visitor: CommandVisitor, context: any): any {
|
visit(visitor: CommandVisitor, context: any): any {
|
||||||
return visitor.visitEmbeddedTemplate(this, context);
|
return visitor.visitEmbeddedTemplate(this, context);
|
||||||
}
|
}
|
||||||
@ -146,20 +149,13 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
|||||||
|
|
||||||
export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||||
directives: Type[], isMerged: boolean, ngContentIndex: number,
|
directives: Type[], isMerged: boolean, ngContentIndex: number,
|
||||||
children: TemplateCmd[]): EmbeddedTemplateCmd {
|
changeDetectorFactory: Function, children: TemplateCmd[]):
|
||||||
|
EmbeddedTemplateCmd {
|
||||||
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
|
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
|
||||||
ngContentIndex, children);
|
ngContentIndex, changeDetectorFactory, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandVisitor extends RenderCommandVisitor {
|
export interface CommandVisitor extends RenderCommandVisitor {}
|
||||||
visitText(cmd: TextCmd, context: any): any;
|
|
||||||
visitNgContent(cmd: NgContentCmd, context: any): any;
|
|
||||||
visitBeginElement(cmd: BeginElementCmd, context: any): any;
|
|
||||||
visitEndElement(context: any): any;
|
|
||||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
|
|
||||||
visitEndComponent(context: any): any;
|
|
||||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
||||||
context: any = null) {
|
context: any = null) {
|
||||||
|
@ -36,7 +36,7 @@ class Directive extends DirectiveMetadata {
|
|||||||
*/
|
*/
|
||||||
class Component extends ComponentMetadata {
|
class Component extends ComponentMetadata {
|
||||||
const Component({String selector, List<String> properties,
|
const Component({String selector, List<String> properties,
|
||||||
List<String> events, Map<String, String> host, bool dynamicLoadable,
|
List<String> events, Map<String, String> host,
|
||||||
List bindings, String exportAs, String moduleId,
|
List bindings, String exportAs, String moduleId,
|
||||||
Map<String, dynamic> queries,
|
Map<String, dynamic> queries,
|
||||||
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
||||||
@ -45,7 +45,6 @@ class Component extends ComponentMetadata {
|
|||||||
properties: properties,
|
properties: properties,
|
||||||
events: events,
|
events: events,
|
||||||
host: host,
|
host: host,
|
||||||
dynamicLoadable: dynamicLoadable,
|
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: exportAs,
|
exportAs: exportAs,
|
||||||
moduleId: moduleId,
|
moduleId: moduleId,
|
||||||
|
@ -76,8 +76,8 @@ export interface ComponentDecorator extends TypeDecorator {
|
|||||||
View(obj: {
|
View(obj: {
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
directives?: Array<Type | any | any[]>,
|
directives?: Array<Type | any[]>,
|
||||||
pipes?: Array<Type | any | any[]>,
|
pipes?: Array<Type | any[]>,
|
||||||
renderer?: string,
|
renderer?: string,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
@ -96,8 +96,8 @@ export interface ViewDecorator extends TypeDecorator {
|
|||||||
View(obj: {
|
View(obj: {
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
directives?: Array<Type | any | any[]>,
|
directives?: Array<Type | any[]>,
|
||||||
pipes?: Array<Type | any | any[]>,
|
pipes?: Array<Type | any[]>,
|
||||||
renderer?: string,
|
renderer?: string,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
@ -218,7 +218,6 @@ export interface ComponentFactory {
|
|||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
moduleId?: string,
|
moduleId?: string,
|
||||||
@ -232,7 +231,6 @@ export interface ComponentFactory {
|
|||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
moduleId?: string,
|
moduleId?: string,
|
||||||
@ -290,7 +288,8 @@ export interface ViewFactory {
|
|||||||
(obj: {
|
(obj: {
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
directives?: Array<Type | any | any[]>,
|
directives?: Array<Type | any[]>,
|
||||||
|
pipes?: Array<Type | any[]>,
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
@ -298,7 +297,8 @@ export interface ViewFactory {
|
|||||||
new (obj: {
|
new (obj: {
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
directives?: Array<Type | any | any[]>,
|
directives?: Array<Type | any[]>,
|
||||||
|
pipes?: Array<Type | any[]>,
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
|
@ -826,27 +826,6 @@ export class DirectiveMetadata extends InjectableMetadata {
|
|||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
export class ComponentMetadata extends DirectiveMetadata {
|
export class ComponentMetadata extends DirectiveMetadata {
|
||||||
/**
|
|
||||||
* Declare that this component can be programatically loaded.
|
|
||||||
* Every component that is used in bootstrap, routing, ... has to be
|
|
||||||
* annotated with this.
|
|
||||||
*
|
|
||||||
* ## Example
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'root',
|
|
||||||
* dynamicLoadable: true
|
|
||||||
* })
|
|
||||||
* @View({
|
|
||||||
* template: 'hello world!'
|
|
||||||
* })
|
|
||||||
* class RootComponent {
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
dynamicLoadable: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the used change detection strategy.
|
* Defines the used change detection strategy.
|
||||||
*
|
*
|
||||||
@ -900,14 +879,13 @@ export class ComponentMetadata extends DirectiveMetadata {
|
|||||||
*/
|
*/
|
||||||
viewBindings: any[];
|
viewBindings: any[];
|
||||||
|
|
||||||
constructor({selector, properties, events, host, dynamicLoadable, exportAs, moduleId, bindings,
|
constructor({selector, properties, events, host, exportAs, moduleId, bindings, viewBindings,
|
||||||
queries, viewBindings, changeDetection = ChangeDetectionStrategy.Default,
|
changeDetection = ChangeDetectionStrategy.Default, queries, compileChildren = true}:
|
||||||
compileChildren = true}: {
|
{
|
||||||
selector?: string,
|
selector?: string,
|
||||||
properties?: string[],
|
properties?: string[],
|
||||||
events?: string[],
|
events?: string[],
|
||||||
host?: StringMap<string, string>,
|
host?: StringMap<string, string>,
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
bindings?: any[],
|
bindings?: any[],
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
moduleId?: string,
|
moduleId?: string,
|
||||||
@ -930,7 +908,6 @@ export class ComponentMetadata extends DirectiveMetadata {
|
|||||||
|
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
this.viewBindings = viewBindings;
|
this.viewBindings = viewBindings;
|
||||||
this.dynamicLoadable = dynamicLoadable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,12 +82,9 @@ export class ViewMetadata {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
// TODO(tbosch): use Type | Binding | any[] when Dart supports union types,
|
directives: Array<Type | any[]>;
|
||||||
// as otherwise we would need to import Binding type and Dart would warn
|
|
||||||
// for an unused import.
|
|
||||||
directives: Array<Type | any | any[]>;
|
|
||||||
|
|
||||||
pipes: Array<Type | any | any[]>;
|
pipes: Array<Type | any[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify how the template and the styles should be encapsulated.
|
* Specify how the template and the styles should be encapsulated.
|
||||||
@ -100,8 +97,8 @@ export class ViewMetadata {
|
|||||||
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: {
|
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: {
|
||||||
templateUrl?: string,
|
templateUrl?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
directives?: Array<Type | any | any[]>,
|
directives?: Array<Type | any[]>,
|
||||||
pipes?: Array<Type | any | any[]>,
|
pipes?: Array<Type | any[]>,
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
styles?: string[],
|
styles?: string[],
|
||||||
styleUrls?: string[],
|
styleUrls?: string[],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {isPresent, isBlank, RegExpWrapper, deserializeEnum} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/core/facade/lang';
|
||||||
import {Promise} from 'angular2/src/core/facade/async';
|
import {Promise} from 'angular2/src/core/facade/async';
|
||||||
import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
@ -309,11 +309,8 @@ export enum ViewEncapsulation {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
var encapsulationMap: Map<number, ViewEncapsulation> = MapWrapper.createFromPairs(
|
export var VIEW_ENCAPSULATION_VALUES =
|
||||||
[[0, ViewEncapsulation.Emulated], [1, ViewEncapsulation.Native], [2, ViewEncapsulation.None]]);
|
[ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None];
|
||||||
export function viewEncapsulationFromJson(value: number): ViewEncapsulation {
|
|
||||||
return deserializeEnum(value, encapsulationMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ViewDefinition {
|
export class ViewDefinition {
|
||||||
componentId: string;
|
componentId: string;
|
||||||
@ -409,7 +406,7 @@ export interface RenderNgContentCmd extends RenderBeginCmd { ngContentIndex: num
|
|||||||
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||||
name: string;
|
name: string;
|
||||||
attrNameAndValues: string[];
|
attrNameAndValues: string[];
|
||||||
eventNames: string[];
|
eventTargetAndNames: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||||
@ -422,15 +419,14 @@ export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
|||||||
children: RenderTemplateCmd[];
|
children: RenderTemplateCmd[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tbosch): change ts2dart to allow to use `CMD` as type in these methods!
|
|
||||||
export interface RenderCommandVisitor {
|
export interface RenderCommandVisitor {
|
||||||
visitText /*<CMD extends RenderTextCmd>*/ (cmd: any, context: any): any;
|
visitText(cmd: any, context: any): any;
|
||||||
visitNgContent /*<CMD extends RenderNgContentCmd>*/ (cmd: any, context: any): any;
|
visitNgContent(cmd: any, context: any): any;
|
||||||
visitBeginElement /*<CMD extends RenderBeginElementCmd>*/ (cmd: any, context: any): any;
|
visitBeginElement(cmd: any, context: any): any;
|
||||||
visitEndElement(context: any): any;
|
visitEndElement(context: any): any;
|
||||||
visitBeginComponent /*<CMD extends RenderBeginComponentCmd>*/ (cmd: any, context: any): any;
|
visitBeginComponent(cmd: any, context: any): any;
|
||||||
visitEndComponent(context: any): any;
|
visitEndComponent(context: any): any;
|
||||||
visitEmbeddedTemplate /*<CMD extends RenderEmbeddedTemplateCmd>*/ (cmd: any, context: any): any;
|
visitEmbeddedTemplate(cmd: any, context: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||||||
properties: dm.properties,
|
properties: dm.properties,
|
||||||
events: dm.events,
|
events: dm.events,
|
||||||
host: dm.host,
|
host: dm.host,
|
||||||
dynamicLoadable: dm.dynamicLoadable,
|
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
exportAs: dm.exportAs,
|
exportAs: dm.exportAs,
|
||||||
moduleId: dm.moduleId,
|
moduleId: dm.moduleId,
|
||||||
|
@ -14,9 +14,8 @@ import {
|
|||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata
|
||||||
ChangeDetectionMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
import {
|
import {
|
||||||
@ -60,10 +59,10 @@ export function main() {
|
|||||||
pipes = new TestPipes();
|
pipes = new TestPipes();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function createChangeDetector(template: string, directives: NormalizedDirectiveMetadata[],
|
function createChangeDetector(template: string, directives: CompileDirectiveMetadata[],
|
||||||
protoViewIndex: number = 0): ChangeDetector {
|
protoViewIndex: number = 0): ChangeDetector {
|
||||||
var protoChangeDetectors =
|
var protoChangeDetectors =
|
||||||
createChangeDetectorDefinitions(new TypeMetadata({name: 'SomeComp'}),
|
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
|
||||||
ChangeDetectionStrategy.Default,
|
ChangeDetectionStrategy.Default,
|
||||||
new ChangeDetectorGenConfig(true, true, false, false),
|
new ChangeDetectorGenConfig(true, true, false, false),
|
||||||
parser.parse(template, directives, 'TestComp'))
|
parser.parse(template, directives, 'TestComp'))
|
||||||
@ -97,6 +96,14 @@ export function main() {
|
|||||||
expect(context.eventLog).toEqual(['click']);
|
expect(context.eventLog).toEqual(['click']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle events with targets', () => {
|
||||||
|
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
|
||||||
|
|
||||||
|
eventLocals.set('$event', 'click');
|
||||||
|
changeDetector.handleEvent('window:click', 0, eventLocals);
|
||||||
|
expect(context.eventLog).toEqual(['click']);
|
||||||
|
});
|
||||||
|
|
||||||
it('should watch variables', () => {
|
it('should watch variables', () => {
|
||||||
var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0);
|
var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0);
|
||||||
|
|
||||||
@ -106,10 +113,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should write directive properties', () => {
|
it('should write directive properties', () => {
|
||||||
var dirMeta = new NormalizedDirectiveMetadata({
|
var dirMeta = CompileDirectiveMetadata.create({
|
||||||
type: new TypeMetadata({name: 'SomeDir'}),
|
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: '[dir-prop]',
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']})
|
properties: ['dirProp']
|
||||||
});
|
});
|
||||||
|
|
||||||
var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0);
|
var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0);
|
||||||
@ -119,11 +126,25 @@ export function main() {
|
|||||||
expect(directive.dirProp).toEqual('someValue');
|
expect(directive.dirProp).toEqual('someValue');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should write template directive properties', () => {
|
||||||
|
var dirMeta = CompileDirectiveMetadata.create({
|
||||||
|
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||||
|
selector: '[dir-prop]',
|
||||||
|
properties: ['dirProp']
|
||||||
|
});
|
||||||
|
|
||||||
|
var changeDetector = createChangeDetector('<template [dir-prop]="someProp">', [dirMeta], 0);
|
||||||
|
|
||||||
|
context.someProp = 'someValue';
|
||||||
|
changeDetector.detectChanges();
|
||||||
|
expect(directive.dirProp).toEqual('someValue');
|
||||||
|
});
|
||||||
|
|
||||||
it('should watch directive host properties', () => {
|
it('should watch directive host properties', () => {
|
||||||
var dirMeta = new NormalizedDirectiveMetadata({
|
var dirMeta = CompileDirectiveMetadata.create({
|
||||||
type: new TypeMetadata({name: 'SomeDir'}),
|
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}})
|
host: {'[elProp]': 'dirProp'}
|
||||||
});
|
});
|
||||||
|
|
||||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||||
@ -134,11 +155,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle directive events', () => {
|
it('should handle directive events', () => {
|
||||||
var dirMeta = new NormalizedDirectiveMetadata({
|
var dirMeta = CompileDirectiveMetadata.create({
|
||||||
type: new TypeMetadata({name: 'SomeDir'}),
|
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
changeDetection:
|
host: {'(click)': 'onEvent($event)'}
|
||||||
new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||||
|
@ -21,11 +21,15 @@ import {Promise} from 'angular2/src/core/facade/async';
|
|||||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata
|
||||||
ChangeDetectionMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
import {
|
||||||
|
SourceModule,
|
||||||
|
SourceExpression,
|
||||||
|
SourceExpressions,
|
||||||
|
moduleRef
|
||||||
|
} from 'angular2/src/compiler/source_module';
|
||||||
|
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
|
|
||||||
@ -63,8 +67,8 @@ export function main() {
|
|||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||||
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||||
var type = new TypeMetadata({name: 'SomeComp'});
|
var type = new CompileTypeMetadata({name: 'SomeComp'});
|
||||||
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||||
var factories =
|
var factories =
|
||||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
@ -99,13 +103,13 @@ export function main() {
|
|||||||
|
|
||||||
describe('compileComponentCodeGen', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||||
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])):
|
directives: CompileDirectiveMetadata[] = CONST_EXPR([])):
|
||||||
Promise<string[]> {
|
Promise<string[]> {
|
||||||
var type = new TypeMetadata({name: 'SomeComp'});
|
var type = new CompileTypeMetadata({name: 'SomeComp'});
|
||||||
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||||
var sourceExpression =
|
var sourceExpressions =
|
||||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
var testableModule = createTestableModule(sourceExpression, 0).getSourceWithImports();
|
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
|
||||||
return evalModule(testableModule.source, testableModule.imports, null);
|
return evalModule(testableModule.source, testableModule.imports, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,9 +126,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTestableModule(source: SourceExpression, changeDetectorIndex: number): SourceModule {
|
function createTestableModule(source: SourceExpressions, changeDetectorIndex: number):
|
||||||
|
SourceModule {
|
||||||
var resultExpression =
|
var resultExpression =
|
||||||
`${THIS_MODULE_REF}testChangeDetector((${source.expression})[${changeDetectorIndex}])`;
|
`${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`;
|
||||||
var testableSource = `${source.declarations.join('\n')}
|
var testableSource = `${source.declarations.join('\n')}
|
||||||
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||||
return new SourceModule(null, testableSource);
|
return new SourceModule(null, testableSource);
|
||||||
|
@ -29,9 +29,9 @@ import {
|
|||||||
} from 'angular2/src/core/compiler/template_commands';
|
} from 'angular2/src/core/compiler/template_commands';
|
||||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
NormalizedTemplateMetadata
|
CompileTemplateMetadata
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
@ -61,12 +61,12 @@ export class RootComp {}
|
|||||||
export class SomeDir {}
|
export class SomeDir {}
|
||||||
export class AComp {}
|
export class AComp {}
|
||||||
|
|
||||||
var RootCompTypeMeta =
|
var RootCompTypeMeta = new CompileTypeMetadata(
|
||||||
new TypeMetadata({id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
|
{id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
|
||||||
var SomeDirTypeMeta =
|
var SomeDirTypeMeta =
|
||||||
new TypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
|
new CompileTypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
|
||||||
var ACompTypeMeta =
|
var ACompTypeMeta =
|
||||||
new TypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
|
new CompileTypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
|
||||||
|
|
||||||
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
|
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
|
||||||
|
|
||||||
@ -84,12 +84,12 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
||||||
type?: TypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
selector?: string,
|
selector?: string,
|
||||||
template?: string,
|
template?: string,
|
||||||
encapsulation?: ViewEncapsulation,
|
encapsulation?: ViewEncapsulation,
|
||||||
ngContentSelectors?: string[]
|
ngContentSelectors?: string[]
|
||||||
}): NormalizedDirectiveMetadata {
|
}): CompileDirectiveMetadata {
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = ViewEncapsulation.None;
|
encapsulation = ViewEncapsulation.None;
|
||||||
}
|
}
|
||||||
@ -102,11 +102,11 @@ export function main() {
|
|||||||
if (isBlank(template)) {
|
if (isBlank(template)) {
|
||||||
template = '';
|
template = '';
|
||||||
}
|
}
|
||||||
return new NormalizedDirectiveMetadata({
|
return CompileDirectiveMetadata.create({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: type,
|
type: type,
|
||||||
template: new NormalizedTemplateMetadata({
|
template: new CompileTemplateMetadata({
|
||||||
template: template,
|
template: template,
|
||||||
ngContentSelectors: ngContentSelectors,
|
ngContentSelectors: ngContentSelectors,
|
||||||
encapsulation: encapsulation
|
encapsulation: encapsulation
|
||||||
@ -114,8 +114,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDirective(type: TypeMetadata, selector: string): NormalizedDirectiveMetadata {
|
function createDirective(type: CompileTypeMetadata, selector: string, exportAs: string = null):
|
||||||
return new NormalizedDirectiveMetadata({selector: selector, isComponent: false, type: type});
|
CompileDirectiveMetadata {
|
||||||
|
return CompileDirectiveMetadata.create(
|
||||||
|
{selector: selector, exportAs: exportAs, isComponent: false, type: type});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -159,18 +161,47 @@ export function main() {
|
|||||||
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
|
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp = createComp({
|
var rootComp = createComp({
|
||||||
type: RootCompTypeMeta,
|
type: RootCompTypeMeta,
|
||||||
template: '<div a="b" #some-var="someValue" (click)="someHandler">'
|
template: '<div a="b" #some-var (click)="someHandler" (window:scroll)="scrollTo()">'
|
||||||
});
|
});
|
||||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
run(rootComp, [])
|
||||||
run(rootComp, [dir])
|
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
[
|
[
|
||||||
BEGIN_ELEMENT,
|
BEGIN_ELEMENT,
|
||||||
'div',
|
'div',
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
['click'],
|
[null, 'click', 'window', 'scroll'],
|
||||||
['someVar', 'someValue'],
|
['someVar', '%implicit'],
|
||||||
|
[],
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[END_ELEMENT]
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create element commands with directives',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var rootComp =
|
||||||
|
createComp({type: RootCompTypeMeta, template: '<div a #some-var="someExport">'});
|
||||||
|
var dir = CompileDirectiveMetadata.create({
|
||||||
|
selector: '[a]',
|
||||||
|
exportAs: 'someExport',
|
||||||
|
isComponent: false,
|
||||||
|
type: SomeDirTypeMeta,
|
||||||
|
host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'}
|
||||||
|
});
|
||||||
|
run(rootComp, [dir])
|
||||||
|
.then((data) => {
|
||||||
|
expect(data).toEqual([
|
||||||
|
[
|
||||||
|
BEGIN_ELEMENT,
|
||||||
|
'div',
|
||||||
|
['a', '', 'role', 'button'],
|
||||||
|
[null, 'click', 'window', 'scroll'],
|
||||||
|
['someVar', 0],
|
||||||
['SomeDirType'],
|
['SomeDirType'],
|
||||||
true,
|
true,
|
||||||
null
|
null
|
||||||
@ -214,10 +245,8 @@ export function main() {
|
|||||||
describe('components', () => {
|
describe('components', () => {
|
||||||
|
|
||||||
it('should create component commands', inject([AsyncTestCompleter], (async) => {
|
it('should create component commands', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp = createComp({
|
var rootComp = createComp(
|
||||||
type: RootCompTypeMeta,
|
{type: RootCompTypeMeta, template: '<a a="b" #some-var (click)="someHandler">'});
|
||||||
template: '<a a="b" #some-var="someValue" (click)="someHandler">'
|
|
||||||
});
|
|
||||||
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
|
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
|
||||||
run(rootComp, [comp])
|
run(rootComp, [comp])
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
@ -226,8 +255,8 @@ export function main() {
|
|||||||
BEGIN_COMPONENT,
|
BEGIN_COMPONENT,
|
||||||
'a',
|
'a',
|
||||||
['a', 'b'],
|
['a', 'b'],
|
||||||
['click'],
|
[null, 'click'],
|
||||||
['someVar', 'someValue'],
|
['someVar', 0],
|
||||||
['ACompType'],
|
['ACompType'],
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
@ -305,7 +334,7 @@ export function main() {
|
|||||||
template: '<template a="b" #some-var="someValue"></template>'
|
template: '<template a="b" #some-var="someValue"></template>'
|
||||||
});
|
});
|
||||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||||
run(rootComp, [dir])
|
run(rootComp, [dir], 1)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
[
|
[
|
||||||
@ -315,6 +344,7 @@ export function main() {
|
|||||||
['SomeDirType'],
|
['SomeDirType'],
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
|
'cd1',
|
||||||
[]
|
[]
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
@ -325,10 +355,20 @@ export function main() {
|
|||||||
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp =
|
var rootComp =
|
||||||
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
||||||
run(rootComp, [])
|
run(rootComp, [], 1)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual(
|
expect(data).toEqual([
|
||||||
[[EMBEDDED_TEMPLATE, [], [], [], false, null, [[TEXT, 't', false, null]]]]);
|
[
|
||||||
|
EMBEDDED_TEMPLATE,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
'cd1',
|
||||||
|
[[TEXT, 't', false, null]]
|
||||||
|
]
|
||||||
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -339,10 +379,10 @@ export function main() {
|
|||||||
type: RootCompTypeMeta,
|
type: RootCompTypeMeta,
|
||||||
template: '<template><ng-content></ng-content></template>'
|
template: '<template><ng-content></ng-content></template>'
|
||||||
});
|
});
|
||||||
run(rootComp, [])
|
run(rootComp, [], 1)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
expect(data).toEqual(
|
expect(data).toEqual(
|
||||||
[[EMBEDDED_TEMPLATE, [], [], [], true, null, [[NG_CONTENT, null]]]]);
|
[[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -364,17 +404,21 @@ export function main() {
|
|||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||||
return new CompiledTemplate(directive.type.id, () => []);
|
return new CompiledTemplate(directive.type.id, () => []);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function run(component: NormalizedDirectiveMetadata,
|
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||||
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||||
|
var changeDetectorFactories = [];
|
||||||
|
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||||
|
(function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i);
|
||||||
|
}
|
||||||
var parsedTemplate =
|
var parsedTemplate =
|
||||||
parser.parse(component.template.template, directives, component.type.name);
|
parser.parse(component.template.template, directives, component.type.name);
|
||||||
var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate,
|
var commands = commandCompiler.compileComponentRuntime(
|
||||||
componentTemplateFactory);
|
component, parsedTemplate, changeDetectorFactories, componentTemplateFactory);
|
||||||
return PromiseWrapper.resolve(humanize(commands));
|
return PromiseWrapper.resolve(humanize(commands));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,17 +428,21 @@ export function main() {
|
|||||||
|
|
||||||
describe('compileComponentCodeGen', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||||
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`;
|
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function run(component: NormalizedDirectiveMetadata,
|
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||||
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||||
|
var changeDetectorFactoryExpressions = [];
|
||||||
|
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||||
|
changeDetectorFactoryExpressions.push(codeGenValueFn(['_'], `'cd${i}'`));
|
||||||
|
}
|
||||||
var parsedTemplate =
|
var parsedTemplate =
|
||||||
parser.parse(component.template.template, directives, component.type.name);
|
parser.parse(component.template.template, directives, component.type.name);
|
||||||
var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate,
|
var sourceModule = commandCompiler.compileComponentCodeGen(
|
||||||
componentTemplateFactory);
|
component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory);
|
||||||
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
|
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
|
||||||
return evalModule(testableModule.source, testableModule.imports, null);
|
return evalModule(testableModule.source, testableModule.imports, null);
|
||||||
}
|
}
|
||||||
@ -432,7 +480,7 @@ class CommandHumanizer implements CommandVisitor {
|
|||||||
BEGIN_ELEMENT,
|
BEGIN_ELEMENT,
|
||||||
cmd.name,
|
cmd.name,
|
||||||
cmd.attrNameAndValues,
|
cmd.attrNameAndValues,
|
||||||
cmd.eventNames,
|
cmd.eventTargetAndNames,
|
||||||
cmd.variableNameAndValues,
|
cmd.variableNameAndValues,
|
||||||
cmd.directives.map(checkAndStringifyType),
|
cmd.directives.map(checkAndStringifyType),
|
||||||
cmd.isBound,
|
cmd.isBound,
|
||||||
@ -449,12 +497,11 @@ class CommandHumanizer implements CommandVisitor {
|
|||||||
BEGIN_COMPONENT,
|
BEGIN_COMPONENT,
|
||||||
cmd.name,
|
cmd.name,
|
||||||
cmd.attrNameAndValues,
|
cmd.attrNameAndValues,
|
||||||
cmd.eventNames,
|
cmd.eventTargetAndNames,
|
||||||
cmd.variableNameAndValues,
|
cmd.variableNameAndValues,
|
||||||
cmd.directives.map(checkAndStringifyType),
|
cmd.directives.map(checkAndStringifyType),
|
||||||
cmd.nativeShadow,
|
cmd.nativeShadow,
|
||||||
cmd.ngContentIndex,
|
cmd.ngContentIndex,
|
||||||
// TODO humanizeTemplate(cmd.template)
|
|
||||||
cmd.template.id
|
cmd.template.id
|
||||||
]);
|
]);
|
||||||
return null;
|
return null;
|
||||||
@ -471,6 +518,7 @@ class CommandHumanizer implements CommandVisitor {
|
|||||||
cmd.directives.map(checkAndStringifyType),
|
cmd.directives.map(checkAndStringifyType),
|
||||||
cmd.isMerged,
|
cmd.isMerged,
|
||||||
cmd.ngContentIndex,
|
cmd.ngContentIndex,
|
||||||
|
cmd.changeDetectorFactory(null),
|
||||||
humanize(cmd.children)
|
humanize(cmd.children)
|
||||||
]);
|
]);
|
||||||
return null;
|
return null;
|
||||||
|
@ -13,98 +13,76 @@ import {
|
|||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
NormalizedTemplateMetadata,
|
CompileTemplateMetadata
|
||||||
ChangeDetectionMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||||
|
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DirectiveMetadata', () => {
|
describe('DirectiveMetadata', () => {
|
||||||
var fullTypeMeta: TypeMetadata;
|
var fullTypeMeta: CompileTypeMetadata;
|
||||||
var fullTemplateMeta: NormalizedTemplateMetadata;
|
var fullTemplateMeta: CompileTemplateMetadata;
|
||||||
var fullChangeDetectionMeta: ChangeDetectionMetadata;
|
var fullDirectiveMeta: CompileDirectiveMetadata;
|
||||||
var fullDirectiveMeta: NormalizedDirectiveMetadata;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fullTypeMeta = new TypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
|
fullTypeMeta = new CompileTypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
|
||||||
fullTemplateMeta = new NormalizedTemplateMetadata({
|
fullTemplateMeta = new CompileTemplateMetadata({
|
||||||
encapsulation: ViewEncapsulation.Emulated,
|
encapsulation: ViewEncapsulation.Emulated,
|
||||||
template: '<a></a>',
|
template: '<a></a>',
|
||||||
|
templateUrl: 'someTemplateUrl',
|
||||||
styles: ['someStyle'],
|
styles: ['someStyle'],
|
||||||
styleAbsUrls: ['someStyleUrl'],
|
styleUrls: ['someStyleUrl'],
|
||||||
hostAttributes: {'attr1': 'attrValue2'},
|
|
||||||
ngContentSelectors: ['*']
|
ngContentSelectors: ['*']
|
||||||
});
|
});
|
||||||
fullChangeDetectionMeta = new ChangeDetectionMetadata({
|
fullDirectiveMeta = CompileDirectiveMetadata.create({
|
||||||
changeDetection: ChangeDetectionStrategy.Default,
|
|
||||||
properties: ['someProp'],
|
|
||||||
events: ['someEvent'],
|
|
||||||
hostListeners: {'event1': 'handler1'},
|
|
||||||
hostProperties: {'prop1': 'expr1'},
|
|
||||||
callAfterContentInit: true,
|
|
||||||
callAfterContentChecked: true,
|
|
||||||
callAfterViewInit: true,
|
|
||||||
callAfterViewChecked: true,
|
|
||||||
callOnChanges: true,
|
|
||||||
callDoCheck: true,
|
|
||||||
callOnInit: true
|
|
||||||
});
|
|
||||||
fullDirectiveMeta = new NormalizedDirectiveMetadata({
|
|
||||||
selector: 'someSelector',
|
selector: 'someSelector',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
dynamicLoadable: true,
|
dynamicLoadable: true,
|
||||||
type: fullTypeMeta, template: fullTemplateMeta,
|
type: fullTypeMeta, template: fullTemplateMeta,
|
||||||
changeDetection: fullChangeDetectionMeta,
|
changeDetection: ChangeDetectionStrategy.Default,
|
||||||
|
properties: ['someProp'],
|
||||||
|
events: ['someEvent'],
|
||||||
|
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
|
||||||
|
lifecycleHooks: [LifecycleHooks.OnChanges]
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DirectiveMetadata', () => {
|
describe('DirectiveMetadata', () => {
|
||||||
it('should serialize with full data', () => {
|
it('should serialize with full data', () => {
|
||||||
expect(NormalizedDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
||||||
.toEqual(fullDirectiveMeta);
|
.toEqual(fullDirectiveMeta);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
it('should serialize with no data', () => {
|
||||||
var empty = new NormalizedDirectiveMetadata();
|
var empty = CompileDirectiveMetadata.create();
|
||||||
expect(NormalizedDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('TypeMetadata', () => {
|
describe('TypeMetadata', () => {
|
||||||
it('should serialize with full data',
|
it('should serialize with full data', () => {
|
||||||
() => { expect(TypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta); });
|
expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta);
|
||||||
|
});
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
it('should serialize with no data', () => {
|
||||||
var empty = new TypeMetadata();
|
var empty = new CompileTypeMetadata();
|
||||||
expect(TypeMetadata.fromJson(empty.toJson())).toEqual(empty);
|
expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('TemplateMetadata', () => {
|
describe('TemplateMetadata', () => {
|
||||||
it('should serialize with full data', () => {
|
it('should serialize with full data', () => {
|
||||||
expect(NormalizedTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
|
expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
|
||||||
.toEqual(fullTemplateMeta);
|
.toEqual(fullTemplateMeta);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
it('should serialize with no data', () => {
|
||||||
var empty = new NormalizedTemplateMetadata();
|
var empty = new CompileTemplateMetadata();
|
||||||
expect(NormalizedTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ChangeDetectionMetadata', () => {
|
|
||||||
it('should serialize with full data', () => {
|
|
||||||
expect(ChangeDetectionMetadata.fromJson(fullChangeDetectionMeta.toJson()))
|
|
||||||
.toEqual(fullChangeDetectionMeta);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should serialize with no data', () => {
|
|
||||||
var empty = new ChangeDetectionMetadata();
|
|
||||||
expect(ChangeDetectionMetadata.fromJson(empty.toJson())).toEqual(empty);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -80,22 +80,6 @@ export function main() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ng-non-bindable', () => {
|
|
||||||
it('should ignore text nodes and elements inside of elements with ng-non-bindable', () => {
|
|
||||||
expect(humanizeDom(
|
|
||||||
parser.parse('<div ng-non-bindable>hello<span></span></div>', 'TestComp')))
|
|
||||||
.toEqual([
|
|
||||||
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
|
||||||
[
|
|
||||||
HtmlAttrAst,
|
|
||||||
'ng-non-bindable',
|
|
||||||
'',
|
|
||||||
'TestComp > div:nth-child(0)[ng-non-bindable=]'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('unparse', () => {
|
describe('unparse', () => {
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
|
|
||||||
import {stringify} from 'angular2/src/core/facade/lang';
|
import {stringify} from 'angular2/src/core/facade/lang';
|
||||||
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
View,
|
View,
|
||||||
@ -43,27 +44,20 @@ export function main() {
|
|||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
var meta = resolver.getMetadata(ComponentWithEverything);
|
var meta = resolver.getMetadata(ComponentWithEverything);
|
||||||
expect(meta.selector).toEqual('someSelector');
|
expect(meta.selector).toEqual('someSelector');
|
||||||
|
expect(meta.exportAs).toEqual('someExportAs');
|
||||||
expect(meta.isComponent).toBe(true);
|
expect(meta.isComponent).toBe(true);
|
||||||
expect(meta.dynamicLoadable).toBe(true);
|
expect(meta.dynamicLoadable).toBe(true);
|
||||||
expect(meta.type.runtime).toBe(ComponentWithEverything);
|
expect(meta.type.runtime).toBe(ComponentWithEverything);
|
||||||
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
|
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
|
||||||
expect(meta.type.moduleId).toEqual('someModuleId');
|
expect(meta.type.moduleId).toEqual('someModuleId');
|
||||||
expect(meta.changeDetection.callAfterContentChecked).toBe(true);
|
expect(meta.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
|
||||||
expect(meta.changeDetection.callAfterContentInit).toBe(true);
|
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways);
|
||||||
expect(meta.changeDetection.callAfterViewChecked).toBe(true);
|
expect(meta.properties).toEqual({'someProp': 'someProp'});
|
||||||
expect(meta.changeDetection.callAfterViewInit).toBe(true);
|
expect(meta.events).toEqual({'someEvent': 'someEvent'});
|
||||||
expect(meta.changeDetection.callDoCheck).toBe(true);
|
expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'});
|
||||||
expect(meta.changeDetection.callOnChanges).toBe(true);
|
expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'});
|
||||||
expect(meta.changeDetection.callOnInit).toBe(true);
|
expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
||||||
expect(meta.changeDetection.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways);
|
|
||||||
expect(meta.changeDetection.properties).toEqual(['someProp']);
|
|
||||||
expect(meta.changeDetection.events).toEqual(['someEvent']);
|
|
||||||
expect(meta.changeDetection.hostListeners)
|
|
||||||
.toEqual({'someHostListener': 'someHostListenerExpr'});
|
|
||||||
expect(meta.changeDetection.hostProperties)
|
|
||||||
.toEqual({'someHostProp': 'someHostPropExpr'});
|
|
||||||
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
|
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
|
||||||
expect(meta.template.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
|
||||||
expect(meta.template.styles).toEqual(['someStyle']);
|
expect(meta.template.styles).toEqual(['someStyle']);
|
||||||
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
||||||
expect(meta.template.template).toEqual('someTemplate');
|
expect(meta.template.template).toEqual('someTemplate');
|
||||||
@ -105,7 +99,7 @@ class DirectiveWithoutModuleId {
|
|||||||
'(someHostListener)': 'someHostListenerExpr',
|
'(someHostListener)': 'someHostListenerExpr',
|
||||||
'someHostAttr': 'someHostAttrValue'
|
'someHostAttr': 'someHostAttrValue'
|
||||||
},
|
},
|
||||||
dynamicLoadable: true,
|
exportAs: 'someExportAs',
|
||||||
moduleId: 'someModuleId',
|
moduleId: 'someModuleId',
|
||||||
changeDetection: ChangeDetectionStrategy.CheckAlways
|
changeDetection: ChangeDetectionStrategy.CheckAlways
|
||||||
})
|
})
|
||||||
|
@ -17,14 +17,14 @@ import {SpyXHR} from '../core/spies';
|
|||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
|
||||||
import {CONST_EXPR, isPresent, StringWrapper} from 'angular2/src/core/facade/lang';
|
import {CONST_EXPR, isPresent, isBlank, StringWrapper} from 'angular2/src/core/facade/lang';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||||
import {evalModule} from './eval_module';
|
import {evalModule} from './eval_module';
|
||||||
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
NormalizedTemplateMetadata,
|
CompileTemplateMetadata,
|
||||||
TypeMetadata
|
CompileTypeMetadata
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
|
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
@ -52,152 +52,249 @@ export function main() {
|
|||||||
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
|
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
|
||||||
|
|
||||||
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
function comp(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
||||||
NormalizedDirectiveMetadata {
|
CompileDirectiveMetadata {
|
||||||
return new NormalizedDirectiveMetadata({
|
return CompileDirectiveMetadata.create({
|
||||||
type: new TypeMetadata({id: 23, moduleId: 'someUrl'}),
|
type: new CompileTypeMetadata({id: 23, moduleId: 'someUrl'}),
|
||||||
template: new NormalizedTemplateMetadata(
|
template: new CompileTemplateMetadata(
|
||||||
{styles: styles, styleAbsUrls: styleAbsUrls, encapsulation: encapsulation})
|
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('compileComponentRuntime', () => {
|
describe('compileComponentRuntime', () => {
|
||||||
function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation,
|
var xhrUrlResults;
|
||||||
expectedStyles: string[]) {
|
var xhrCount;
|
||||||
return inject([AsyncTestCompleter], (async) => {
|
|
||||||
|
beforeEach(() => {
|
||||||
|
xhrCount = 0;
|
||||||
|
xhrUrlResults = {};
|
||||||
|
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: blue}';
|
||||||
|
xhrUrlResults[IMPORT_ABS_MODULE_NAME_WITH_IMPORT] =
|
||||||
|
`a {color: green}@import ${IMPORT_REL_MODULE_NAME};`;
|
||||||
|
});
|
||||||
|
|
||||||
|
function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
||||||
|
Promise<string[]> {
|
||||||
// Note: Can't use MockXHR as the xhr is called recursively,
|
// Note: Can't use MockXHR as the xhr is called recursively,
|
||||||
// so we can't trigger flush.
|
// so we can't trigger flush.
|
||||||
xhr.spy('get').andCallFake((url) => {
|
xhr.spy('get').andCallFake((url) => {
|
||||||
var response;
|
var response = xhrUrlResults[url];
|
||||||
if (url == IMPORT_ABS_MODULE_NAME) {
|
xhrCount++;
|
||||||
response = 'span {color: blue}';
|
if (isBlank(response)) {
|
||||||
} else if (url == IMPORT_ABS_MODULE_NAME_WITH_IMPORT) {
|
|
||||||
response = `a {color: green}@import ${IMPORT_REL_MODULE_NAME};`;
|
|
||||||
} else {
|
|
||||||
throw new BaseException(`Unexpected url ${url}`);
|
throw new BaseException(`Unexpected url ${url}`);
|
||||||
}
|
}
|
||||||
return PromiseWrapper.resolve(response);
|
return PromiseWrapper.resolve(response);
|
||||||
});
|
});
|
||||||
compiler.compileComponentRuntime(comp(styles, styleAbsUrls, encapsulation))
|
return compiler.compileComponentRuntime(comp(styles, styleAbsUrls, encapsulation));
|
||||||
.then((value) => {
|
|
||||||
compareStyles(value, expectedStyles);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('no shim', () => {
|
describe('no shim', () => {
|
||||||
var encapsulation = ViewEncapsulation.None;
|
var encapsulation = ViewEncapsulation.None;
|
||||||
|
|
||||||
it('should compile plain css rules',
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {color: red}', 'span {color: blue}'], [], encapsulation,
|
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
|
||||||
['div {color: red}', 'span {color: blue}']));
|
.then(styles => {
|
||||||
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules',
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation,
|
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
|
||||||
['div {color: red}', 'span {color: blue}']));
|
.then(styles => {
|
||||||
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules transitively',
|
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation,
|
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation)
|
||||||
['div {color: red}', 'a {color: green}', 'span {color: blue}']));
|
.then(styles => {
|
||||||
|
expect(styles)
|
||||||
|
.toEqual(['div {color: red}', 'a {color: green}', 'span {color: blue}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with shim', () => {
|
describe('with shim', () => {
|
||||||
var encapsulation = ViewEncapsulation.Emulated;
|
var encapsulation = ViewEncapsulation.Emulated;
|
||||||
|
|
||||||
it('should compile plain css rules',
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(
|
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
|
||||||
['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation,
|
.then(styles => {
|
||||||
['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}']));
|
expect(styles).toEqual([
|
||||||
|
'div[_ngcontent-23] {\ncolor: red;\n}',
|
||||||
|
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules',
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(
|
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
|
||||||
['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation,
|
.then(styles => {
|
||||||
['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}']));
|
expect(styles).toEqual([
|
||||||
|
'div[_ngcontent-23] {\ncolor: red;\n}',
|
||||||
|
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules transitively',
|
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation, [
|
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation)
|
||||||
|
.then(styles => {
|
||||||
|
expect(styles).toEqual([
|
||||||
'div[_ngcontent-23] {\ncolor: red;\n}',
|
'div[_ngcontent-23] {\ncolor: red;\n}',
|
||||||
'a[_ngcontent-23] {\ncolor: green;\n}',
|
'a[_ngcontent-23] {\ncolor: green;\n}',
|
||||||
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
||||||
]));
|
]);
|
||||||
|
async.done();
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compileComponentCodeGen', () => {
|
it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
|
||||||
function runTest(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation,
|
PromiseWrapper.all([
|
||||||
expectedStyles: string[]) {
|
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None),
|
||||||
return inject([AsyncTestCompleter], (async) => {
|
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
|
||||||
var sourceExpression =
|
])
|
||||||
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
|
.then((styleArrays) => {
|
||||||
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
expect(styleArrays[0]).toEqual(['span {color: blue}']);
|
||||||
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
|
expect(styleArrays[1]).toEqual(['span {color: blue}']);
|
||||||
.then((value) => {
|
expect(xhrCount).toBe(1);
|
||||||
compareStyles(value, expectedStyles);
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
|
||||||
|
.then((styles0) => {
|
||||||
|
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}';
|
||||||
|
return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
|
||||||
|
.then((styles1) => {
|
||||||
|
expect(styles0).toEqual(['span {color: blue}']);
|
||||||
|
expect(styles1).toEqual(['span {color: blue}']);
|
||||||
|
expect(xhrCount).toBe(1);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}));
|
||||||
|
|
||||||
|
it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
|
||||||
|
.then((_) => {
|
||||||
|
compiler.clearCache();
|
||||||
|
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}';
|
||||||
|
return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None);
|
||||||
|
})
|
||||||
|
.then((styles) => {
|
||||||
|
expect(xhrCount).toBe(2);
|
||||||
|
expect(styles).toEqual(['span {color: black}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('compileComponentCodeGen', () => {
|
||||||
|
function compile(styles: string[], styleAbsUrls: string[], encapsulation: ViewEncapsulation):
|
||||||
|
Promise<string[]> {
|
||||||
|
var sourceExpression =
|
||||||
|
compiler.compileComponentCodeGen(comp(styles, styleAbsUrls, encapsulation));
|
||||||
|
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
|
||||||
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
||||||
|
};
|
||||||
|
|
||||||
describe('no shim', () => {
|
describe('no shim', () => {
|
||||||
var encapsulation = ViewEncapsulation.None;
|
var encapsulation = ViewEncapsulation.None;
|
||||||
|
|
||||||
it('should compile plain css ruless',
|
it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {color: red}', 'span {color: blue}'], [], encapsulation,
|
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
|
||||||
['div {color: red}', 'span {color: blue}']));
|
.then(styles => {
|
||||||
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should compile css rules with newlines and quotes',
|
it('should compile css rules with newlines and quotes',
|
||||||
runTest(['div\n{"color": \'red\'}'], [], encapsulation, ['div\n{"color": \'red\'}']));
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile(['div\n{"color": \'red\'}'], [], encapsulation)
|
||||||
|
.then(styles => {
|
||||||
|
expect(styles).toEqual(['div\n{"color": \'red\'}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules',
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation,
|
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
|
||||||
['div {color: red}', 'span {color: blue}']),
|
.then(styles => {
|
||||||
1000);
|
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with shim', () => {
|
describe('with shim', () => {
|
||||||
var encapsulation = ViewEncapsulation.Emulated;
|
var encapsulation = ViewEncapsulation.Emulated;
|
||||||
|
|
||||||
it('should compile plain css ruless',
|
it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(
|
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
|
||||||
['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation,
|
.then(styles => {
|
||||||
['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}']));
|
expect(styles).toEqual([
|
||||||
|
'div[_ngcontent-23] {\ncolor: red;\n}',
|
||||||
|
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules',
|
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
|
||||||
runTest(
|
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
|
||||||
['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation,
|
.then(styles => {
|
||||||
['div[_ngcontent-23] {\ncolor: red;\n}', 'span[_ngcontent-23] {\ncolor: blue;\n}']),
|
expect(styles).toEqual([
|
||||||
1000);
|
'div[_ngcontent-23] {\ncolor: red;\n}',
|
||||||
|
'span[_ngcontent-23] {\ncolor: blue;\n}'
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compileStylesheetCodeGen', () => {
|
describe('compileStylesheetCodeGen', () => {
|
||||||
function runTest(style: string, expectedStyles: string[], expectedShimStyles: string[]) {
|
function compile(style: string): Promise<string[]> {
|
||||||
return inject([AsyncTestCompleter], (async) => {
|
|
||||||
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
|
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
|
||||||
PromiseWrapper.all(sourceModules.map(sourceModule => {
|
return PromiseWrapper.all(sourceModules.map(sourceModule => {
|
||||||
var sourceWithImports =
|
var sourceWithImports = testableModule(sourceModule).getSourceWithImports();
|
||||||
testableModule(sourceModule).getSourceWithImports();
|
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
|
||||||
return evalModule(sourceWithImports.source, sourceWithImports.imports,
|
}));
|
||||||
null);
|
|
||||||
}))
|
|
||||||
.then((values) => {
|
|
||||||
compareStyles(values[0], expectedStyles);
|
|
||||||
compareStyles(values[1], expectedShimStyles);
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should compile plain css rules', runTest('div {color: red;}', ['div {color: red;}'],
|
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
|
||||||
['div[_ngcontent-%COMP%] {\ncolor: red;\n}']));
|
compile('div {color: red;}')
|
||||||
|
.then(stylesAndShimStyles => {
|
||||||
|
expect(stylesAndShimStyles)
|
||||||
|
.toEqual(
|
||||||
|
[['div {color: red;}'], ['div[_ngcontent-%COMP%] {\ncolor: red;\n}']]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should allow to import rules with relative paths',
|
it('should allow to import rules with relative paths',
|
||||||
runTest(`div {color: red}@import ${IMPORT_REL_MODULE_NAME};`,
|
inject([AsyncTestCompleter], (async) => {
|
||||||
['div {color: red}', 'span {color: blue}'], [
|
compile(`div {color: red}@import ${IMPORT_REL_MODULE_NAME};`)
|
||||||
|
.then(stylesAndShimStyles => {
|
||||||
|
expect(stylesAndShimStyles)
|
||||||
|
.toEqual([
|
||||||
|
['div {color: red}', 'span {color: blue}'],
|
||||||
|
[
|
||||||
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
|
||||||
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
|
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
|
||||||
]));
|
]
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,7 @@ import {
|
|||||||
TemplateCompiler,
|
TemplateCompiler,
|
||||||
NormalizedComponentWithViewDirectives
|
NormalizedComponentWithViewDirectives
|
||||||
} from 'angular2/src/compiler/template_compiler';
|
} from 'angular2/src/compiler/template_compiler';
|
||||||
import {
|
import {CompileDirectiveMetadata} from 'angular2/src/compiler/directive_metadata';
|
||||||
DirectiveMetadata,
|
|
||||||
NormalizedDirectiveMetadata,
|
|
||||||
INormalizedDirectiveMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
|
||||||
import {evalModule} from './eval_module';
|
import {evalModule} from './eval_module';
|
||||||
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
|
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
|
||||||
import {XHR} from 'angular2/src/core/render/xhr';
|
import {XHR} from 'angular2/src/core/render/xhr';
|
||||||
@ -104,6 +100,18 @@ export function main() {
|
|||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should pass the right change detector to embedded templates',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
compile([CompWithEmbeddedTemplate])
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<template>');
|
||||||
|
expect(humanizedTemplate['commands'][1]['commands'][1]['cd'])
|
||||||
|
.toEqual(['elementProperty(href)=someCtxValue']);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('compileHostComponentRuntime', () => {
|
describe('compileHostComponentRuntime', () => {
|
||||||
@ -113,28 +121,53 @@ export function main() {
|
|||||||
|
|
||||||
runTests(compile);
|
runTests(compile);
|
||||||
|
|
||||||
it('should cache components', inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
it('should cache components for parallel requests',
|
||||||
// we expect only one request!
|
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
xhr.expect('angular2/test/compiler/compUrl.html', '');
|
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
|
||||||
PromiseWrapper.all([
|
PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])])
|
||||||
compiler.compileHostComponentRuntime(CompWithTemplateUrl),
|
|
||||||
compiler.compileHostComponentRuntime(CompWithTemplateUrl)
|
|
||||||
])
|
|
||||||
.then((humanizedTemplates) => {
|
.then((humanizedTemplates) => {
|
||||||
expect(humanizedTemplates[0]).toEqual(humanizedTemplates[1]);
|
expect(humanizedTemplates[0]['commands'][1]['commands']).toEqual(['#text(a)']);
|
||||||
|
expect(humanizedTemplates[1]['commands'][1]['commands']).toEqual(['#text(a)']);
|
||||||
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
xhr.flush();
|
xhr.flush();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should only allow dynamic loadable components', () => {
|
it('should cache components for sequential requests',
|
||||||
expect(() => compiler.compileHostComponentRuntime(PlainDirective))
|
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
.toThrowError(
|
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
|
||||||
`Could not compile '${stringify(PlainDirective)}' because it is not dynamically loadable.`);
|
compile([CompWithTemplateUrl])
|
||||||
expect(() => compiler.compileHostComponentRuntime(CompWithoutHost))
|
.then((humanizedTemplate0) => {
|
||||||
.toThrowError(
|
return compile([CompWithTemplateUrl])
|
||||||
`Could not compile '${stringify(CompWithoutHost)}' because it is not dynamically loadable.`);
|
.then((humanizedTemplate1) => {
|
||||||
|
expect(humanizedTemplate0['commands'][1]['commands'])
|
||||||
|
.toEqual(['#text(a)']);
|
||||||
|
expect(humanizedTemplate1['commands'][1]['commands'])
|
||||||
|
.toEqual(['#text(a)']);
|
||||||
|
async.done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow to clear the cache',
|
||||||
|
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||||
|
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
|
||||||
|
compile([CompWithTemplateUrl])
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
compiler.clearCache();
|
||||||
|
xhr.expect('angular2/test/compiler/compUrl.html', 'b');
|
||||||
|
var result = compile([CompWithTemplateUrl]);
|
||||||
|
xhr.flush();
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.then((humanizedTemplate) => {
|
||||||
|
expect(humanizedTemplate['commands'][1]['commands']).toEqual(['#text(b)']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
xhr.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,7 +178,7 @@ export function main() {
|
|||||||
runtimeMetadataResolver.getViewDirectivesMetadata(component));
|
runtimeMetadataResolver.getViewDirectivesMetadata(component));
|
||||||
return PromiseWrapper.all(compAndViewDirMetas.map(
|
return PromiseWrapper.all(compAndViewDirMetas.map(
|
||||||
meta => compiler.normalizeDirectiveMetadata(meta)))
|
meta => compiler.normalizeDirectiveMetadata(meta)))
|
||||||
.then((normalizedCompAndViewDirMetas: NormalizedDirectiveMetadata[]) =>
|
.then((normalizedCompAndViewDirMetas: CompileDirectiveMetadata[]) =>
|
||||||
new NormalizedComponentWithViewDirectives(
|
new NormalizedComponentWithViewDirectives(
|
||||||
normalizedCompAndViewDirMetas[0],
|
normalizedCompAndViewDirMetas[0],
|
||||||
normalizedCompAndViewDirMetas.slice(1)));
|
normalizedCompAndViewDirMetas.slice(1)));
|
||||||
@ -173,15 +206,15 @@ export function main() {
|
|||||||
it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => {
|
it('should serialize and deserialize', inject([AsyncTestCompleter], (async) => {
|
||||||
compiler.normalizeDirectiveMetadata(
|
compiler.normalizeDirectiveMetadata(
|
||||||
runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles))
|
runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles))
|
||||||
.then((meta: NormalizedDirectiveMetadata) => {
|
.then((meta: CompileDirectiveMetadata) => {
|
||||||
var json = compiler.serializeDirectiveMetadata(meta);
|
var json = compiler.serializeDirectiveMetadata(meta);
|
||||||
expect(isString(json)).toBe(true);
|
expect(isString(json)).toBe(true);
|
||||||
// Note: serializing will clear our the runtime type!
|
// Note: serializing will clear our the runtime type!
|
||||||
var clonedMeta =
|
var clonedMeta = compiler.deserializeDirectiveMetadata(json);
|
||||||
<NormalizedDirectiveMetadata>compiler.deserializeDirectiveMetadata(json);
|
|
||||||
expect(meta.changeDetection).toEqual(clonedMeta.changeDetection);
|
expect(meta.changeDetection).toEqual(clonedMeta.changeDetection);
|
||||||
expect(meta.template).toEqual(clonedMeta.template);
|
expect(meta.template).toEqual(clonedMeta.template);
|
||||||
expect(meta.selector).toEqual(clonedMeta.selector);
|
expect(meta.selector).toEqual(clonedMeta.selector);
|
||||||
|
expect(meta.exportAs).toEqual(clonedMeta.exportAs);
|
||||||
expect(meta.type.name).toEqual(clonedMeta.type.name);
|
expect(meta.type.name).toEqual(clonedMeta.type.name);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
@ -194,7 +227,7 @@ export function main() {
|
|||||||
xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate');
|
xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate');
|
||||||
compiler.normalizeDirectiveMetadata(
|
compiler.normalizeDirectiveMetadata(
|
||||||
runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
|
runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
|
||||||
.then((meta: NormalizedDirectiveMetadata) => {
|
.then((meta: CompileDirectiveMetadata) => {
|
||||||
expect(meta.template.template).toEqual('loadedTemplate');
|
expect(meta.template.template).toEqual('loadedTemplate');
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
@ -203,13 +236,19 @@ export function main() {
|
|||||||
|
|
||||||
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
|
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
|
||||||
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
|
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
|
||||||
compiler.normalizeDirectiveMetadata(meta)
|
compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => {
|
||||||
.then((normMeta: NormalizedDirectiveMetadata) => {
|
|
||||||
expect(normMeta.selector).toEqual(meta.selector);
|
|
||||||
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
|
|
||||||
expect(normMeta.isComponent).toEqual(meta.isComponent);
|
|
||||||
expect(normMeta.type).toEqual(meta.type);
|
expect(normMeta.type).toEqual(meta.type);
|
||||||
|
expect(normMeta.isComponent).toEqual(meta.isComponent);
|
||||||
|
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
|
||||||
|
expect(normMeta.selector).toEqual(meta.selector);
|
||||||
|
expect(normMeta.exportAs).toEqual(meta.exportAs);
|
||||||
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
|
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
|
||||||
|
expect(normMeta.properties).toEqual(meta.properties);
|
||||||
|
expect(normMeta.events).toEqual(meta.events);
|
||||||
|
expect(normMeta.hostListeners).toEqual(meta.hostListeners);
|
||||||
|
expect(normMeta.hostProperties).toEqual(meta.hostProperties);
|
||||||
|
expect(normMeta.hostAttributes).toEqual(meta.hostAttributes);
|
||||||
|
expect(normMeta.lifecycleHooks).toEqual(meta.lifecycleHooks);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -233,24 +272,30 @@ export function main() {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'comp-a',
|
selector: 'comp-a',
|
||||||
dynamicLoadable: true,
|
|
||||||
host: {'[title]': 'someProp'},
|
host: {'[title]': 'someProp'},
|
||||||
moduleId: THIS_MODULE
|
moduleId: THIS_MODULE,
|
||||||
|
exportAs: 'someExportAs'
|
||||||
})
|
})
|
||||||
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
|
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
|
||||||
class CompWithBindingsAndStyles {
|
class CompWithBindingsAndStyles {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'tree', dynamicLoadable: true, moduleId: THIS_MODULE})
|
@Component({selector: 'tree', moduleId: THIS_MODULE})
|
||||||
@View({template: '<tree></tree>', directives: [TreeComp]})
|
@View({template: '<tree></tree>', directives: [TreeComp]})
|
||||||
class TreeComp {
|
class TreeComp {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'comp-url', dynamicLoadable: true, moduleId: THIS_MODULE})
|
@Component({selector: 'comp-url', moduleId: THIS_MODULE})
|
||||||
@View({templateUrl: 'compUrl.html'})
|
@View({templateUrl: 'compUrl.html'})
|
||||||
class CompWithTemplateUrl {
|
class CompWithTemplateUrl {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE})
|
||||||
|
@View({template: '<template><a [href]="someProp"></a></template>'})
|
||||||
|
class CompWithEmbeddedTemplate {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Directive({selector: 'plain', moduleId: THIS_MODULE})
|
@Directive({selector: 'plain', moduleId: THIS_MODULE})
|
||||||
class PlainDirective {
|
class PlainDirective {
|
||||||
}
|
}
|
||||||
@ -260,9 +305,8 @@ class PlainDirective {
|
|||||||
class CompWithoutHost {
|
class CompWithoutHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testableTemplateModule(sourceModule: SourceModule, comp: INormalizedDirectiveMetadata):
|
function testableTemplateModule(sourceModule: SourceModule, normComp: CompileDirectiveMetadata):
|
||||||
SourceModule {
|
SourceModule {
|
||||||
var normComp = <NormalizedDirectiveMetadata>comp;
|
|
||||||
var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`;
|
var resultExpression = `${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template)`;
|
||||||
var testableSource = `${sourceModule.sourceWithModuleRefs}
|
var testableSource = `${sourceModule.sourceWithModuleRefs}
|
||||||
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||||
@ -290,7 +334,7 @@ export function humanizeTemplate(template: CompiledTemplate,
|
|||||||
result = {
|
result = {
|
||||||
'styles': template.styles,
|
'styles': template.styles,
|
||||||
'commands': commands,
|
'commands': commands,
|
||||||
'cd': testChangeDetector(template.changeDetectorFactories[0])
|
'cd': testChangeDetector(template.changeDetectorFactory)
|
||||||
};
|
};
|
||||||
humanizedTemplates.set(template.id, result);
|
humanizedTemplates.set(template.id, result);
|
||||||
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
|
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
|
||||||
@ -316,7 +360,10 @@ function testChangeDetector(changeDetectorFactory: Function): string[] {
|
|||||||
class CommandHumanizer implements CommandVisitor {
|
class CommandHumanizer implements CommandVisitor {
|
||||||
constructor(private result: any[],
|
constructor(private result: any[],
|
||||||
private humanizedTemplates: Map<number, StringMap<string, any>>) {}
|
private humanizedTemplates: Map<number, StringMap<string, any>>) {}
|
||||||
visitText(cmd: TextCmd, context: any): any { return null; }
|
visitText(cmd: TextCmd, context: any): any {
|
||||||
|
this.result.push(`#text(${cmd.value})`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
||||||
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||||
this.result.push(`<${cmd.name}>`);
|
this.result.push(`<${cmd.name}>`);
|
||||||
@ -332,5 +379,10 @@ class CommandHumanizer implements CommandVisitor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitEndComponent(context: any): any { return this.visitEndElement(context); }
|
visitEndComponent(context: any): any { return this.visitEndElement(context); }
|
||||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any { return null; }
|
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
|
||||||
|
this.result.push(`<template>`);
|
||||||
|
this.result.push({'cd': testChangeDetector(cmd.changeDetectorFactory)});
|
||||||
|
this.result.push(`</template>`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,8 @@ import {
|
|||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
NormalizedTemplateMetadata,
|
CompileTemplateMetadata
|
||||||
TemplateMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||||
|
|
||||||
@ -27,27 +26,29 @@ import {TEST_BINDINGS} from './test_bindings';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('TemplateNormalizer', () => {
|
describe('TemplateNormalizer', () => {
|
||||||
var dirType: TypeMetadata;
|
var dirType: CompileTypeMetadata;
|
||||||
|
|
||||||
beforeEachBindings(() => TEST_BINDINGS);
|
beforeEachBindings(() => TEST_BINDINGS);
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
() => { dirType = new TypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'}); });
|
dirType = new CompileTypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'});
|
||||||
|
});
|
||||||
|
|
||||||
describe('loadTemplate', () => {
|
describe('loadTemplate', () => {
|
||||||
describe('inline template', () => {
|
describe('inline template', () => {
|
||||||
it('should store the template',
|
it('should store the template',
|
||||||
inject([AsyncTestCompleter, TemplateNormalizer],
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
(async, normalizer: TemplateNormalizer) => {
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: 'a',
|
template: 'a',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
|
expect(template.templateUrl).toEqual('some/module/id');
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -55,15 +56,15 @@ export function main() {
|
|||||||
it('should resolve styles on the annotation against the moduleId',
|
it('should resolve styles on the annotation against the moduleId',
|
||||||
inject([AsyncTestCompleter, TemplateNormalizer],
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
(async, normalizer: TemplateNormalizer) => {
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '',
|
template: '',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -71,15 +72,15 @@ export function main() {
|
|||||||
it('should resolve styles in the template against the moduleId',
|
it('should resolve styles in the template against the moduleId',
|
||||||
inject([AsyncTestCompleter, TemplateNormalizer],
|
inject([AsyncTestCompleter, TemplateNormalizer],
|
||||||
(async, normalizer: TemplateNormalizer) => {
|
(async, normalizer: TemplateNormalizer) => {
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: '<style>@import test.css</style>',
|
template: '<style>@import test.css</style>',
|
||||||
templateUrl: null,
|
templateUrl: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
@ -91,15 +92,16 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
xhr.expect('some/module/sometplurl', 'a');
|
xhr.expect('some/module/sometplurl', 'a');
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'sometplurl',
|
templateUrl: 'sometplurl',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.template).toEqual('a');
|
expect(template.template).toEqual('a');
|
||||||
|
expect(template.templateUrl).toEqual('some/module/sometplurl');
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
xhr.flush();
|
xhr.flush();
|
||||||
@ -109,15 +111,15 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
xhr.expect('some/module/tpl/sometplurl', '');
|
xhr.expect('some/module/tpl/sometplurl', '');
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'tpl/sometplurl',
|
templateUrl: 'tpl/sometplurl',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: ['test.css']
|
styleUrls: ['test.css']
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
xhr.flush();
|
xhr.flush();
|
||||||
@ -127,15 +129,15 @@ export function main() {
|
|||||||
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
|
||||||
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
|
||||||
xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>');
|
xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>');
|
||||||
normalizer.normalizeTemplate(dirType, new TemplateMetadata({
|
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
template: null,
|
template: null,
|
||||||
templateUrl: 'tpl/sometplurl',
|
templateUrl: 'tpl/sometplurl',
|
||||||
styles: [],
|
styles: [],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
}))
|
}))
|
||||||
.then((template: NormalizedTemplateMetadata) => {
|
.then((template: CompileTemplateMetadata) => {
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/tpl/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/tpl/test.css']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
xhr.flush();
|
xhr.flush();
|
||||||
@ -151,8 +153,8 @@ export function main() {
|
|||||||
|
|
||||||
var viewEncapsulation = ViewEncapsulation.Native;
|
var viewEncapsulation = ViewEncapsulation.Native;
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
dirType, new CompileTemplateMetadata(
|
||||||
new TemplateMetadata({encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
|
{encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
|
||||||
'', 'some/module/');
|
'', 'some/module/');
|
||||||
expect(template.encapsulation).toBe(viewEncapsulation);
|
expect(template.encapsulation).toBe(viewEncapsulation);
|
||||||
}));
|
}));
|
||||||
@ -160,88 +162,90 @@ export function main() {
|
|||||||
it('should keep the template as html',
|
it('should keep the template as html',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
|
||||||
'some/module/');
|
'some/module/');
|
||||||
expect(template.template).toEqual('a')
|
expect(template.template).toEqual('a')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect and keep ngContent',
|
it('should collect ngContent',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<ng-content select="a"></ng-content>', 'some/module/');
|
'<ng-content select="a"></ng-content>', 'some/module/');
|
||||||
expect(template.ngContentSelectors).toEqual(['a']);
|
expect(template.ngContentSelectors).toEqual(['a']);
|
||||||
expect(template.template).toEqual('<ng-content select="a"></ng-content>');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should normalize ngContent wildcard selector',
|
it('should normalize ngContent wildcard selector',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
|
||||||
'some/module/');
|
'some/module/');
|
||||||
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect and remove top level styles in the template',
|
it('should collect top level styles in the template',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<style>a</style>', 'some/module/');
|
'<style>a</style>', 'some/module/');
|
||||||
expect(template.styles).toEqual(['a']);
|
expect(template.styles).toEqual(['a']);
|
||||||
expect(template.template).toEqual('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect and remove styles inside in elements',
|
it('should collect styles inside in elements',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<div><style>a</style></div>', 'some/module/');
|
'<div><style>a</style></div>', 'some/module/');
|
||||||
expect(template.styles).toEqual(['a']);
|
expect(template.styles).toEqual(['a']);
|
||||||
expect(template.template).toEqual('<div></div>');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect and remove styleUrls in the template',
|
it('should collect styleUrls in the template',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<link rel="stylesheet" href="aUrl">', 'some/module/');
|
'<link rel="stylesheet" href="aUrl">', 'some/module/');
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
|
expect(template.styleUrls).toEqual(['some/module/aUrl']);
|
||||||
expect(template.template).toEqual('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should collect and remove styleUrls in elements',
|
it('should collect styleUrls in elements',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/');
|
'<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/');
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/aUrl']);
|
expect(template.styleUrls).toEqual(['some/module/aUrl']);
|
||||||
expect(template.template).toEqual('<div></div>');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should keep link elements with non stylesheet rel attribute',
|
it('should ignore link elements with non stylesheet rel attribute',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
'<link href="b" rel="a"></link>', 'some/module/');
|
'<link href="b" rel="a"></link>', 'some/module/');
|
||||||
expect(template.styleAbsUrls).toEqual([]);
|
expect(template.styleUrls).toEqual([]);
|
||||||
expect(template.template).toEqual('<link href="b" rel="a"></link>');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should extract @import style urls into styleAbsUrl',
|
it('should extract @import style urls into styleAbsUrl',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata(
|
dirType, new CompileTemplateMetadata(
|
||||||
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
|
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
|
||||||
'', 'some/module/id');
|
'', 'some/module/id');
|
||||||
expect(template.styles).toEqual(['']);
|
expect(template.styles).toEqual(['']);
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/test.css']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should resolve relative urls in inline styles',
|
it('should resolve relative urls in inline styles',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType, new TemplateMetadata({
|
dirType, new CompileTemplateMetadata({
|
||||||
encapsulation: null,
|
encapsulation: null,
|
||||||
styles: ['.foo{background-image: url(\'double.jpg\');'],
|
styles: ['.foo{background-image: url(\'double.jpg\');'],
|
||||||
styleUrls: []
|
styleUrls: []
|
||||||
@ -254,15 +258,32 @@ export function main() {
|
|||||||
it('should resolve relative style urls in styleUrls',
|
it('should resolve relative style urls in styleUrls',
|
||||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
var template = normalizer.normalizeLoadedTemplate(
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
dirType,
|
dirType, new CompileTemplateMetadata(
|
||||||
new TemplateMetadata({encapsulation: null, styles: [], styleUrls: ['test.css']}), '',
|
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
|
||||||
'some/module/id');
|
'', 'some/module/id');
|
||||||
expect(template.styles).toEqual([]);
|
expect(template.styles).toEqual([]);
|
||||||
expect(template.styleAbsUrls).toEqual(['some/module/test.css']);
|
expect(template.styleUrls).toEqual(['some/module/test.css']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no stlyes nor stylesheets',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType, new CompileTemplateMetadata(
|
||||||
|
{encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}),
|
||||||
|
'', 'some/module/id');
|
||||||
|
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should ignore elements with ng-non-bindable attribute and their children',
|
||||||
|
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||||
|
var template = normalizer.normalizeLoadedTemplate(
|
||||||
|
dirType,
|
||||||
|
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||||
|
'<div ng-non-bindable><ng-content select="a"></ng-content></div><ng-content ng-non-bindable select="b"></ng-content>',
|
||||||
|
'some/module/');
|
||||||
|
expect(template.ngContentSelectors).toEqual([]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,9 @@ import {TEST_BINDINGS} from './test_bindings';
|
|||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
|
||||||
import {
|
import {
|
||||||
NormalizedDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
TypeMetadata,
|
CompileTypeMetadata,
|
||||||
ChangeDetectionMetadata,
|
CompileTemplateMetadata
|
||||||
NormalizedTemplateMetadata
|
|
||||||
} from 'angular2/src/compiler/directive_metadata';
|
} from 'angular2/src/compiler/directive_metadata';
|
||||||
import {
|
import {
|
||||||
templateVisitAll,
|
templateVisitAll,
|
||||||
@ -59,14 +58,14 @@ export function main() {
|
|||||||
|
|
||||||
beforeEach(inject([TemplateParser], (_parser) => {
|
beforeEach(inject([TemplateParser], (_parser) => {
|
||||||
parser = _parser;
|
parser = _parser;
|
||||||
ngIf = new NormalizedDirectiveMetadata({
|
ngIf = CompileDirectiveMetadata.create({
|
||||||
selector: '[ng-if]',
|
selector: '[ng-if]',
|
||||||
type: new TypeMetadata({name: 'NgIf'}),
|
type: new CompileTypeMetadata({name: 'NgIf'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['ngIf']})
|
properties: ['ngIf']
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function parse(template: string, directives: NormalizedDirectiveMetadata[]): TemplateAst[] {
|
function parse(template: string, directives: CompileDirectiveMetadata[]): TemplateAst[] {
|
||||||
return parser.parse(template, directives, 'TestComp');
|
return parser.parse(template, directives, 'TestComp');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,68 +318,39 @@ export function main() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('variables', () => {
|
|
||||||
|
|
||||||
it('should parse variables via #... and not report them as attributes', () => {
|
|
||||||
expect(humanizeTemplateAsts(parse('<div #a="b">', [])))
|
|
||||||
.toEqual([
|
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
|
||||||
[VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]']
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should parse variables via var-... and not report them as attributes', () => {
|
|
||||||
expect(humanizeTemplateAsts(parse('<div var-a="b">', [])))
|
|
||||||
.toEqual([
|
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
|
||||||
[VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[var-a=b]']
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should camel case variables', () => {
|
|
||||||
expect(humanizeTemplateAsts(parse('<div var-some-a="b">', [])))
|
|
||||||
.toEqual([
|
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
|
||||||
[VariableAst, 'someA', 'b', 'TestComp > div:nth-child(0)[var-some-a=b]']
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use $implicit as variable name if none was specified', () => {
|
|
||||||
expect(humanizeTemplateAsts(parse('<div var-a>', [])))
|
|
||||||
.toEqual([
|
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
|
||||||
[VariableAst, 'a', '$implicit', 'TestComp > div:nth-child(0)[var-a=]']
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('directives', () => {
|
describe('directives', () => {
|
||||||
it('should locate directives ordered by name and components first', () => {
|
it('should locate directives components first and ordered by the directives array in the View',
|
||||||
var dirA = new NormalizedDirectiveMetadata(
|
() => {
|
||||||
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
var dirB = new NormalizedDirectiveMetadata(
|
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA', id: 3})});
|
||||||
{selector: '[a]', type: new TypeMetadata({name: 'DirB'})});
|
var dirB = CompileDirectiveMetadata.create(
|
||||||
var comp = new NormalizedDirectiveMetadata({
|
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB', id: 2})});
|
||||||
|
var dirC = CompileDirectiveMetadata.create(
|
||||||
|
{selector: '[c]', type: new CompileTypeMetadata({name: 'DirC', id: 1})});
|
||||||
|
var comp = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'ZComp'}),
|
type: new CompileTypeMetadata({name: 'ZComp'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div a="b">', [dirB, dirA, comp])))
|
expect(humanizeTemplateAsts(parse('<div a c b>', [dirA, dirB, dirC, comp])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
[AttrAst, 'a', 'b', 'TestComp > div:nth-child(0)[a=b]'],
|
[AttrAst, 'a', '', 'TestComp > div:nth-child(0)[a=]'],
|
||||||
|
[AttrAst, 'b', '', 'TestComp > div:nth-child(0)[b=]'],
|
||||||
|
[AttrAst, 'c', '', 'TestComp > div:nth-child(0)[c=]'],
|
||||||
[DirectiveAst, comp, 'TestComp > div:nth-child(0)'],
|
[DirectiveAst, comp, 'TestComp > div:nth-child(0)'],
|
||||||
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
||||||
[DirectiveAst, dirB, 'TestComp > div:nth-child(0)']
|
[DirectiveAst, dirB, 'TestComp > div:nth-child(0)'],
|
||||||
|
[DirectiveAst, dirC, 'TestComp > div:nth-child(0)']
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in property bindings', () => {
|
it('should locate directives in property bindings', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata(
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
{selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})});
|
||||||
var dirB = new NormalizedDirectiveMetadata(
|
var dirB = CompileDirectiveMetadata.create(
|
||||||
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div [a]="b">', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
@ -397,23 +367,23 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in variable bindings', () => {
|
it('should locate directives in variable bindings', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata(
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
{selector: '[a=b]', exportAs: 'b', type: new CompileTypeMetadata({name: 'DirA'})});
|
||||||
var dirB = new NormalizedDirectiveMetadata(
|
var dirB = CompileDirectiveMetadata.create(
|
||||||
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div #a="b">', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
[VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]'],
|
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
||||||
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)']
|
[VariableAst, 'a', 'b', 'TestComp > div:nth-child(0)[#a=b]']
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive host properties', () => {
|
it('should parse directive host properties', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({hostProperties: {'a': 'expr'}})
|
host: {'[a]': 'expr'}
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
@ -431,10 +401,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive host listeners', () => {
|
it('should parse directive host listeners', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({hostListeners: {'a': 'expr'}})
|
host: {'(a)': 'expr'}
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
@ -445,10 +415,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse directive properties', () => {
|
it('should parse directive properties', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['aProp']})
|
properties: ['aProp']
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div [a-prop]="expr"></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
@ -464,10 +434,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse renamed directive properties', () => {
|
it('should parse renamed directive properties', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['b:a']})
|
properties: ['b:a']
|
||||||
});
|
});
|
||||||
expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div [a]="expr"></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
@ -478,11 +448,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse literal directive properties', () => {
|
it('should parse literal directive properties', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
selector: 'div',
|
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']});
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
|
||||||
});
|
|
||||||
expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div a="literal"></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
@ -498,11 +465,8 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support optional directive properties', () => {
|
it('should support optional directive properties', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
selector: 'div',
|
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), properties: ['a']});
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
|
||||||
});
|
|
||||||
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
expect(humanizeTemplateAsts(parse('<div></div>', [dirA])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
@ -512,6 +476,83 @@ export function main() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('variables', () => {
|
||||||
|
|
||||||
|
it('should parse variables via #... and not report them as attributes', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div #a>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse variables via var-... and not report them as attributes', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div var-a>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', '', 'TestComp > div:nth-child(0)[var-a=]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should camel case variables', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div var-some-a>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'someA', '', 'TestComp > div:nth-child(0)[var-some-a=]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign variables with empty value to element', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div #a></div>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign variables to directives via exportAs', () => {
|
||||||
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
|
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'}), exportAs: 'dirA'});
|
||||||
|
expect(humanizeTemplateAsts(parse('<div #a="dirA"></div>', [dirA])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', 'dirA', 'TestComp > div:nth-child(0)[#a=dirA]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report variables with values that dont match a directive as errors', () => {
|
||||||
|
expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
|
||||||
|
There is no directive with "exportAs" set to "dirA" at TestComp > div:nth-child(0)[#a=dirA]`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow variables with values that dont match a directive on embedded template elements',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<template #a="b"></template>', [])))
|
||||||
|
.toEqual([
|
||||||
|
[EmbeddedTemplateAst, 'TestComp > template:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', 'b', 'TestComp > template:nth-child(0)[#a=b]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign variables with empty value to components', () => {
|
||||||
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
|
selector: '[a]',
|
||||||
|
isComponent: true,
|
||||||
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
|
exportAs: 'dirA', template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
|
});
|
||||||
|
expect(humanizeTemplateAsts(parse('<div #a></div>', [dirA])))
|
||||||
|
.toEqual([
|
||||||
|
[ElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]'],
|
||||||
|
[DirectiveAst, dirA, 'TestComp > div:nth-child(0)'],
|
||||||
|
[VariableAst, 'a', '', 'TestComp > div:nth-child(0)[#a=]']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('explicit templates', () => {
|
describe('explicit templates', () => {
|
||||||
it('should create embedded templates for <template> elements', () => {
|
it('should create embedded templates for <template> elements', () => {
|
||||||
expect(humanizeTemplateAsts(parse('<template></template>', [])))
|
expect(humanizeTemplateAsts(parse('<template></template>', [])))
|
||||||
@ -563,13 +604,13 @@ export function main() {
|
|||||||
|
|
||||||
describe('directives', () => {
|
describe('directives', () => {
|
||||||
it('should locate directives in property bindings', () => {
|
it('should locate directives in property bindings', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: '[a=b]',
|
selector: '[a=b]',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['a']})
|
properties: ['a']
|
||||||
});
|
});
|
||||||
var dirB = new NormalizedDirectiveMetadata(
|
var dirB = CompileDirectiveMetadata.create(
|
||||||
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div template="a b" b>', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
||||||
@ -587,10 +628,10 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should locate directives in variable bindings', () => {
|
it('should locate directives in variable bindings', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata(
|
var dirA = CompileDirectiveMetadata.create(
|
||||||
{selector: '[a=b]', type: new TypeMetadata({name: 'DirA'})});
|
{selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})});
|
||||||
var dirB = new NormalizedDirectiveMetadata(
|
var dirB = CompileDirectiveMetadata.create(
|
||||||
{selector: '[b]', type: new TypeMetadata({name: 'DirB'})});
|
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
|
||||||
expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB])))
|
expect(humanizeTemplateAsts(parse('<div template="#a=b" b>', [dirA, dirB])))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
[EmbeddedTemplateAst, 'TestComp > div:nth-child(0)'],
|
||||||
@ -624,12 +665,12 @@ export function main() {
|
|||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
function createComp(selector: string, ngContentSelectors: string[]):
|
function createComp(selector: string, ngContentSelectors: string[]):
|
||||||
NormalizedDirectiveMetadata {
|
CompileDirectiveMetadata {
|
||||||
return new NormalizedDirectiveMetadata({
|
return CompileDirectiveMetadata.create({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'SomeComp'}),
|
type: new CompileTypeMetadata({name: 'SomeComp'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: ngContentSelectors})
|
template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,38 +766,38 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp > div:nth-ch
|
|||||||
|
|
||||||
it('should not throw on invalid property names if the property is used by a directive',
|
it('should not throw on invalid property names if the property is used by a directive',
|
||||||
() => {
|
() => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
changeDetection: new ChangeDetectionMetadata({properties: ['invalidProp']})
|
properties: ['invalidProp']
|
||||||
});
|
});
|
||||||
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
|
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow more than 1 component per element', () => {
|
it('should not allow more than 1 component per element', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
var dirB = new NormalizedDirectiveMetadata({
|
var dirB = CompileDirectiveMetadata.create({
|
||||||
selector: 'div',
|
selector: 'div',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'DirB'}),
|
type: new CompileTypeMetadata({name: 'DirB'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
|
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
|
||||||
More than one component: DirA,DirB in TestComp > div:nth-child(0)`);
|
More than one component: DirB,DirA in TestComp > div:nth-child(0)`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow components or element nor event bindings on explicit embedded templates',
|
it('should not allow components or element nor event bindings on explicit embedded templates',
|
||||||
() => {
|
() => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: '[a]',
|
selector: '[a]',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
|
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
|
||||||
.toThrowError(`Template parse errors:
|
.toThrowError(`Template parse errors:
|
||||||
@ -766,17 +807,45 @@ Event binding e on an embedded template in TestComp > template:nth-child(0)[(e)=
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow components or element bindings on inline embedded templates', () => {
|
it('should not allow components or element bindings on inline embedded templates', () => {
|
||||||
var dirA = new NormalizedDirectiveMetadata({
|
var dirA = CompileDirectiveMetadata.create({
|
||||||
selector: '[a]',
|
selector: '[a]',
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
type: new TypeMetadata({name: 'DirA'}),
|
type: new CompileTypeMetadata({name: 'DirA'}),
|
||||||
template: new NormalizedTemplateMetadata({ngContentSelectors: []})
|
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||||
});
|
});
|
||||||
expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
|
expect(() => parse('<div *a="b">', [dirA])).toThrowError(`Template parse errors:
|
||||||
Components on an embedded template: DirA in TestComp > div:nth-child(0)
|
Components on an embedded template: DirA in TestComp > div:nth-child(0)
|
||||||
Property binding a not used by any directive on an embedded template in TestComp > div:nth-child(0)[*a=b]`);
|
Property binding a not used by any directive on an embedded template in TestComp > div:nth-child(0)[*a=b]`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ignore elements', () => {
|
||||||
|
it('should ignore <script> elements but include them for source info', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<script></script>a', [])))
|
||||||
|
.toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore <style> elements but include them for source info', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<style></style>a', [])))
|
||||||
|
.toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore <link rel="stylesheet"> elements but include them for source info', () => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<link rel="stylesheet"></link>a', [])))
|
||||||
|
.toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore elements with ng-non-bindable, including their children, but include them for source info',
|
||||||
|
() => {
|
||||||
|
expect(humanizeTemplateAsts(parse('<div ng-non-bindable>b</div>a', [])))
|
||||||
|
.toEqual([[TextAst, 'a', 'TestComp > #text(a):nth-child(1)']]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,7 +874,7 @@ class TemplateHumanizer implements TemplateAstVisitor {
|
|||||||
templateVisitAll(this, ast.attrs);
|
templateVisitAll(this, ast.attrs);
|
||||||
templateVisitAll(this, ast.properties);
|
templateVisitAll(this, ast.properties);
|
||||||
templateVisitAll(this, ast.events);
|
templateVisitAll(this, ast.events);
|
||||||
templateVisitAll(this, ast.vars);
|
templateVisitAll(this, ast.exportAsVars);
|
||||||
templateVisitAll(this, ast.directives);
|
templateVisitAll(this, ast.directives);
|
||||||
templateVisitAll(this, ast.children);
|
templateVisitAll(this, ast.children);
|
||||||
return null;
|
return null;
|
||||||
@ -852,6 +921,7 @@ class TemplateHumanizer implements TemplateAstVisitor {
|
|||||||
templateVisitAll(this, ast.properties);
|
templateVisitAll(this, ast.properties);
|
||||||
templateVisitAll(this, ast.hostProperties);
|
templateVisitAll(this, ast.hostProperties);
|
||||||
templateVisitAll(this, ast.hostEvents);
|
templateVisitAll(this, ast.hostEvents);
|
||||||
|
templateVisitAll(this, ast.exportAsVars);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
|
||||||
|
@ -397,23 +397,6 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should use the last directive binding per directive',
|
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
|
||||||
tcb.overrideView(MyComp, new ViewMetadata({
|
|
||||||
template: '<p no-duplicate></p>',
|
|
||||||
directives: [
|
|
||||||
bind(DuplicateDir)
|
|
||||||
.toClass(DuplicateDir),
|
|
||||||
bind(DuplicateDir).toClass(OtherDuplicateDir)
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
.createAsync(MyComp)
|
|
||||||
.then((rootTC) => {
|
|
||||||
expect(rootTC.debugElement.nativeElement).toHaveText('othernoduplicate');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support directives where a selector matches property binding',
|
it('should support directives where a selector matches property binding',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideView(MyComp, new ViewMetadata(
|
tcb.overrideView(MyComp, new ViewMetadata(
|
||||||
@ -436,27 +419,11 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should allow specifying directives as bindings',
|
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
|
||||||
tcb.overrideView(MyComp, new ViewMetadata({
|
|
||||||
template: '<child-cmp></child-cmp>',
|
|
||||||
directives: [bind(ChildComp).toClass(ChildComp)]
|
|
||||||
}))
|
|
||||||
|
|
||||||
.createAsync(MyComp)
|
|
||||||
.then((rootTC) => {
|
|
||||||
rootTC.detectChanges();
|
|
||||||
|
|
||||||
expect(rootTC.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should read directives metadata from their binding token',
|
it('should read directives metadata from their binding token',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideView(MyComp, new ViewMetadata({
|
tcb.overrideView(MyComp, new ViewMetadata({
|
||||||
template: '<div public-api><div needs-public-api></div></div>',
|
template: '<div public-api><div needs-public-api></div></div>',
|
||||||
directives: [bind(PublicApi).toClass(PrivateImpl), NeedsPublicApi]
|
directives: [PrivateImpl, NeedsPublicApi]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
.createAsync(MyComp)
|
.createAsync(MyComp)
|
||||||
@ -2043,12 +2010,14 @@ class NeedsAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[public-api]'})
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class PublicApi {
|
class PublicApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[private-impl]'})
|
@Directive({
|
||||||
|
selector: '[public-api]',
|
||||||
|
bindings: [new Binding(PublicApi, {toAlias: PrivateImpl, deps: []})]
|
||||||
|
})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class PrivateImpl extends PublicApi {
|
class PrivateImpl extends PublicApi {
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,7 @@ export function main() {
|
|||||||
@Component({selector: 'app', viewBindings: [forwardRef(() => Frame)]})
|
@Component({selector: 'app', viewBindings: [forwardRef(() => Frame)]})
|
||||||
@View({
|
@View({
|
||||||
template: `<door><lock></lock></door>`,
|
template: `<door><lock></lock></door>`,
|
||||||
directives: [
|
directives: [forwardRef(() => Door), forwardRef(() => Lock)]
|
||||||
bind(forwardRef(() => Door))
|
|
||||||
.toClass(forwardRef(() => Door)),
|
|
||||||
bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock))
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
class App {
|
class App {
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user